chore: lint project

This commit is contained in:
Bereket Engida
2024-09-26 08:14:28 +03:00
parent 4d879263da
commit d4f4b94386
360 changed files with 16260 additions and 16182 deletions

View File

@@ -19,5 +19,8 @@
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"[astro]": { "[astro]": {
"editor.defaultFormatter": "biomejs.biome" "editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
} }
} }

View File

@@ -3,12 +3,12 @@
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -18,111 +18,111 @@ import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
export default function Component() { export default function Component() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
setIsSubmitting(true); setIsSubmitting(true);
setError(""); setError("");
// Simulate API call // Simulate API call
try { try {
const res = await client.forgetPassword({ const res = await client.forgetPassword({
email, email,
}); });
// If the API call is successful, set isSubmitted to true // If the API call is successful, set isSubmitted to true
setIsSubmitted(true); setIsSubmitted(true);
} catch (err) { } catch (err) {
setError("An error occurred. Please try again."); setError("An error occurred. Please try again.");
} finally { } finally {
setIsSubmitting(false); setIsSubmitting(false);
} }
}; };
if (isSubmitted) { if (isSubmitted) {
return ( return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]"> <main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
<Card className="w-[350px]"> <Card className="w-[350px]">
<CardHeader> <CardHeader>
<CardTitle>Check your email</CardTitle> <CardTitle>Check your email</CardTitle>
<CardDescription> <CardDescription>
We've sent a password reset link to your email. We've sent a password reset link to your email.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Alert> <Alert>
<CheckCircle2 className="h-4 w-4" /> <CheckCircle2 className="h-4 w-4" />
<AlertDescription> <AlertDescription>
If you don't see the email, check your spam folder. If you don't see the email, check your spam folder.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button <Button
variant="outline" variant="outline"
className="w-full" className="w-full"
onClick={() => setIsSubmitted(false)} onClick={() => setIsSubmitted(false)}
> >
<ArrowLeft className="mr-2 h-4 w-4" /> Back to reset password <ArrowLeft className="mr-2 h-4 w-4" /> Back to reset password
</Button> </Button>
</CardFooter> </CardFooter>
</Card> </Card>
</main> </main>
); );
} }
return ( return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]"> <main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
{/* Radial gradient for the container to give a faded look */} {/* Radial gradient for the container to give a faded look */}
<div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div> <div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div>
<Card className="w-[350px]"> <Card className="w-[350px]">
<CardHeader> <CardHeader>
<CardTitle>Forgot password</CardTitle> <CardTitle>Forgot password</CardTitle>
<CardDescription> <CardDescription>
Enter your email to reset your password Enter your email to reset your password
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="grid w-full items-center gap-4"> <div className="grid w-full items-center gap-4">
<div className="flex flex-col space-y-1.5"> <div className="flex flex-col space-y-1.5">
<Label htmlFor="email">Email</Label> <Label htmlFor="email">Email</Label>
<Input <Input
id="email" id="email"
type="email" type="email"
placeholder="Enter your email" placeholder="Enter your email"
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
required required
/> />
</div> </div>
</div> </div>
{error && ( {error && (
<Alert variant="destructive" className="mt-4"> <Alert variant="destructive" className="mt-4">
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription> <AlertDescription>{error}</AlertDescription>
</Alert> </Alert>
)} )}
<Button <Button
className="w-full mt-4" className="w-full mt-4"
type="submit" type="submit"
disabled={isSubmitting} disabled={isSubmitting}
> >
{isSubmitting ? "Sending..." : "Send reset link"} {isSubmitting ? "Sending..." : "Send reset link"}
</Button> </Button>
</form> </form>
</CardContent> </CardContent>
<CardFooter className="flex justify-center"> <CardFooter className="flex justify-center">
<Link href="/sign-in"> <Link href="/sign-in">
<Button variant="link" className="px-0"> <Button variant="link" className="px-0">
Back to sign in Back to sign in
</Button> </Button>
</Link> </Link>
</CardFooter> </CardFooter>
</Card> </Card>
</main> </main>
); );
} }

View File

@@ -3,12 +3,12 @@
import { Alert, AlertDescription } from "@/components/ui/alert"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { PasswordInput } from "@/components/ui/password-input"; import { PasswordInput } from "@/components/ui/password-input";
@@ -19,78 +19,78 @@ import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
export default function ResetPassword({ export default function ResetPassword({
params, params,
}: { }: {
params: { token: string }; params: { token: string };
}) { }) {
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const token = params.token; const token = params.token;
const router = useRouter(); const router = useRouter();
async function handleSubmit(e: React.FormEvent) { async function handleSubmit(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
setIsSubmitting(true); setIsSubmitting(true);
setError(""); setError("");
const res = await client.resetPassword({ const res = await client.resetPassword({
newPassword: password, newPassword: password,
}); });
if (res.error) { if (res.error) {
toast.error(res.error.message); toast.error(res.error.message);
} }
setIsSubmitting(false); setIsSubmitting(false);
router.push("/sign-in"); router.push("/sign-in");
} }
return ( return (
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]"> <div className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
<Card className="w-[350px]"> <Card className="w-[350px]">
<CardHeader> <CardHeader>
<CardTitle>Reset password</CardTitle> <CardTitle>Reset password</CardTitle>
<CardDescription> <CardDescription>
Enter new password and confirm it to reset your password Enter new password and confirm it to reset your password
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="grid w-full items-center gap-2"> <div className="grid w-full items-center gap-2">
<div className="flex flex-col space-y-1.5"> <div className="flex flex-col space-y-1.5">
<Label htmlFor="email">New password</Label> <Label htmlFor="email">New password</Label>
<PasswordInput <PasswordInput
id="password" id="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
autoComplete="password" autoComplete="password"
placeholder="Password" placeholder="Password"
/> />
</div> </div>
<div className="flex flex-col space-y-1.5"> <div className="flex flex-col space-y-1.5">
<Label htmlFor="email">Confirm password</Label> <Label htmlFor="email">Confirm password</Label>
<PasswordInput <PasswordInput
id="password" id="password"
value={confirmPassword} value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)} onChange={(e) => setConfirmPassword(e.target.value)}
autoComplete="password" autoComplete="password"
placeholder="Password" placeholder="Password"
/> />
</div> </div>
</div> </div>
{error && ( {error && (
<Alert variant="destructive" className="mt-4"> <Alert variant="destructive" className="mt-4">
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription> <AlertDescription>{error}</AlertDescription>
</Alert> </Alert>
)} )}
<Button <Button
className="w-full mt-4" className="w-full mt-4"
type="submit" type="submit"
disabled={isSubmitting} disabled={isSubmitting}
> >
{isSubmitting ? "Resetting..." : "Reset password"} {isSubmitting ? "Resetting..." : "Reset password"}
</Button> </Button>
</form> </form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
); );
} }

View File

@@ -5,26 +5,26 @@ import { SignUp } from "@/components/sign-up";
import { Tabs } from "@/components/ui/tabs2"; import { Tabs } from "@/components/ui/tabs2";
export default function Page() { export default function Page() {
return ( return (
<div className="w-full"> <div className="w-full">
<div className="flex items-center flex-col justify-center w-full md:py-10"> <div className="flex items-center flex-col justify-center w-full md:py-10">
<div className="md:w-[400px]"> <div className="md:w-[400px]">
<Tabs <Tabs
tabs={[ tabs={[
{ {
title: "Sign In", title: "Sign In",
value: "sign-in", value: "sign-in",
content: <SignIn />, content: <SignIn />,
}, },
{ {
title: "Sign Up", title: "Sign Up",
value: "sign-up", value: "sign-up",
content: <SignUp />, content: <SignUp />,
}, },
]} ]}
/> />
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -2,12 +2,12 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -17,105 +17,105 @@ import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
export default function Component() { export default function Component() {
const [otp, setOtp] = useState(""); const [otp, setOtp] = useState("");
const [isOtpSent, setIsOtpSent] = useState(false); const [isOtpSent, setIsOtpSent] = useState(false);
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const [isError, setIsError] = useState(false); const [isError, setIsError] = useState(false);
const [isValidated, setIsValidated] = useState(false); const [isValidated, setIsValidated] = useState(false);
const [OTP, setOTP] = useState(""); const [OTP, setOTP] = useState("");
// In a real app, this email would come from your authentication context // In a real app, this email would come from your authentication context
const userEmail = "user@example.com"; const userEmail = "user@example.com";
const requestOTP = async () => { const requestOTP = async () => {
const res = await client.twoFactor.sendOtp({ const res = await client.twoFactor.sendOtp({
fetchOptions: { fetchOptions: {
body: { body: {
returnOTP: true, returnOTP: true,
}, },
}, },
}); });
setOTP(res.data?.OTP || ""); setOTP(res.data?.OTP || "");
// In a real app, this would call your backend API to send the OTP // In a real app, this would call your backend API to send the OTP
setMessage("OTP sent to your email"); setMessage("OTP sent to your email");
setIsError(false); setIsError(false);
setIsOtpSent(true); setIsOtpSent(true);
}; };
const router = useRouter(); const router = useRouter();
const validateOTP = async () => { const validateOTP = async () => {
const res = await client.twoFactor.verifyOtp({ const res = await client.twoFactor.verifyOtp({
code: otp, code: otp,
}); });
if (res.data) { if (res.data) {
setMessage("OTP validated successfully"); setMessage("OTP validated successfully");
setIsError(false); setIsError(false);
setIsValidated(true); setIsValidated(true);
router.push("/"); router.push("/");
} else { } else {
setIsError(true); setIsError(true);
setMessage("Invalid OTP"); setMessage("Invalid OTP");
} }
}; };
return ( return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]"> <main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
<Card className="w-[350px]"> <Card className="w-[350px]">
<CardHeader> <CardHeader>
<CardTitle>Two-Factor Authentication</CardTitle> <CardTitle>Two-Factor Authentication</CardTitle>
<CardDescription> <CardDescription>
Verify your identity with a one-time password Verify your identity with a one-time password
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid w-full items-center gap-4"> <div className="grid w-full items-center gap-4">
{!isOtpSent ? ( {!isOtpSent ? (
<Button onClick={requestOTP} className="w-full"> <Button onClick={requestOTP} className="w-full">
<Mail className="mr-2 h-4 w-4" /> Send OTP to Email <Mail className="mr-2 h-4 w-4" /> Send OTP to Email
</Button> </Button>
) : ( ) : (
<> <>
<div className="flex flex-col space-y-1.5"> <div className="flex flex-col space-y-1.5">
<Label htmlFor="otp">One-Time Password</Label> <Label htmlFor="otp">One-Time Password</Label>
<Label className="py-2"> <Label className="py-2">
Use{" "} Use{" "}
<span className="text-blue-100 bg-slate-800 px-2"> <span className="text-blue-100 bg-slate-800 px-2">
{OTP} {OTP}
</span>{" "} </span>{" "}
(on real app, this would be sent to your email) (on real app, this would be sent to your email)
</Label> </Label>
<Input <Input
id="otp" id="otp"
placeholder="Enter 6-digit OTP" placeholder="Enter 6-digit OTP"
value={otp} value={otp}
onChange={(e) => setOtp(e.target.value)} onChange={(e) => setOtp(e.target.value)}
maxLength={6} maxLength={6}
/> />
</div> </div>
<Button <Button
onClick={validateOTP} onClick={validateOTP}
disabled={otp.length !== 6 || isValidated} disabled={otp.length !== 6 || isValidated}
> >
Validate OTP Validate OTP
</Button> </Button>
</> </>
)} )}
</div> </div>
{message && ( {message && (
<div <div
className={`flex items-center gap-2 mt-4 ${ className={`flex items-center gap-2 mt-4 ${
isError ? "text-red-500" : "text-primary" isError ? "text-red-500" : "text-primary"
}`} }`}
> >
{isError ? ( {isError ? (
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
) : ( ) : (
<CheckCircle2 className="h-4 w-4" /> <CheckCircle2 className="h-4 w-4" />
)} )}
<p className="text-sm">{message}</p> <p className="text-sm">{message}</p>
</div> </div>
)} )}
</CardContent> </CardContent>
</Card> </Card>
</main> </main>
); );
} }

View File

@@ -2,12 +2,12 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -17,81 +17,81 @@ import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
export default function Component() { export default function Component() {
const [totpCode, setTotpCode] = useState(""); const [totpCode, setTotpCode] = useState("");
const [error, setError] = useState(""); const [error, setError] = useState("");
const [success, setSuccess] = useState(false); const [success, setSuccess] = useState(false);
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (totpCode.length !== 6 || !/^\d+$/.test(totpCode)) { if (totpCode.length !== 6 || !/^\d+$/.test(totpCode)) {
setError("TOTP code must be 6 digits"); setError("TOTP code must be 6 digits");
return; return;
} }
client.twoFactor client.twoFactor
.verifyTotp({ .verifyTotp({
code: totpCode, code: totpCode,
}) })
.then((res) => { .then((res) => {
if (res.data?.status) { if (res.data?.status) {
setSuccess(true); setSuccess(true);
setError(""); setError("");
} else { } else {
setError("Invalid TOTP code"); setError("Invalid TOTP code");
} }
}); });
}; };
return ( return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]"> <main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
<Card className="w-[350px]"> <Card className="w-[350px]">
<CardHeader> <CardHeader>
<CardTitle>TOTP Verification</CardTitle> <CardTitle>TOTP Verification</CardTitle>
<CardDescription> <CardDescription>
Enter your 6-digit TOTP code to authenticate Enter your 6-digit TOTP code to authenticate
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{!success ? ( {!success ? (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="totp">TOTP Code</Label> <Label htmlFor="totp">TOTP Code</Label>
<Input <Input
id="totp" id="totp"
type="text" type="text"
inputMode="numeric" inputMode="numeric"
pattern="\d{6}" pattern="\d{6}"
maxLength={6} maxLength={6}
value={totpCode} value={totpCode}
onChange={(e) => setTotpCode(e.target.value)} onChange={(e) => setTotpCode(e.target.value)}
placeholder="Enter 6-digit code" placeholder="Enter 6-digit code"
required required
/> />
</div> </div>
{error && ( {error && (
<div className="flex items-center mt-2 text-red-500"> <div className="flex items-center mt-2 text-red-500">
<AlertCircle className="w-4 h-4 mr-2" /> <AlertCircle className="w-4 h-4 mr-2" />
<span className="text-sm">{error}</span> <span className="text-sm">{error}</span>
</div> </div>
)} )}
<Button type="submit" className="w-full mt-4"> <Button type="submit" className="w-full mt-4">
Verify Verify
</Button> </Button>
</form> </form>
) : ( ) : (
<div className="flex flex-col items-center justify-center space-y-2"> <div className="flex flex-col items-center justify-center space-y-2">
<CheckCircle2 className="w-12 h-12 text-green-500" /> <CheckCircle2 className="w-12 h-12 text-green-500" />
<p className="text-lg font-semibold">Verification Successful</p> <p className="text-lg font-semibold">Verification Successful</p>
</div> </div>
)} )}
</CardContent> </CardContent>
<CardFooter className="text-sm text-muted-foreground gap-2"> <CardFooter className="text-sm text-muted-foreground gap-2">
<Link href="/two-factor/otp"> <Link href="/two-factor/otp">
<Button variant="link" size="sm"> <Button variant="link" size="sm">
Switch to Email Verification Switch to Email Verification
</Button> </Button>
</Link> </Link>
</CardFooter> </CardFooter>
</Card> </Card>
</main> </main>
); );
} }

View File

@@ -1,31 +1,43 @@
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card" import {
import { Button } from "@/components/ui/button" Card,
import { AlertCircle } from "lucide-react" CardHeader,
import Link from "next/link" CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { AlertCircle } from "lucide-react";
import Link from "next/link";
export function InvitationError() { export function InvitationError() {
return ( return (
<Card className="w-full max-w-md mx-auto"> <Card className="w-full max-w-md mx-auto">
<CardHeader> <CardHeader>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<AlertCircle className="w-6 h-6 text-destructive" /> <AlertCircle className="w-6 h-6 text-destructive" />
<CardTitle className="text-xl text-destructive">Invitation Error</CardTitle> <CardTitle className="text-xl text-destructive">
</div> Invitation Error
<CardDescription> </CardTitle>
There was an issue with your invitation. </div>
</CardDescription> <CardDescription>
</CardHeader> There was an issue with your invitation.
<CardContent> </CardDescription>
<p className="mb-4 text-sm text-muted-foreground"> </CardHeader>
The invitation you're trying to access is either invalid or you don't have the correct permissions. <CardContent>
Please check your email for a valid invitation or contact the person who sent it. <p className="mb-4 text-sm text-muted-foreground">
</p> The invitation you're trying to access is either invalid or you don't
</CardContent> have the correct permissions. Please check your email for a valid
<CardFooter> invitation or contact the person who sent it.
<Link href="/" className="w-full"> </p>
<Button variant="outline" className="w-full">Go back to home</Button> </CardContent>
</Link> <CardFooter>
</CardFooter> <Link href="/" className="w-full">
</Card> <Button variant="outline" className="w-full">
) Go back to home
</Button>
</Link>
</CardFooter>
</Card>
);
} }

View File

@@ -2,12 +2,12 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { CheckIcon, XIcon } from "lucide-react"; import { CheckIcon, XIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@@ -18,169 +18,169 @@ import { InvitationError } from "./invitation-error";
import { Invitation } from "@/lib/auth-types"; import { Invitation } from "@/lib/auth-types";
export default function InvitationPage({ export default function InvitationPage({
params, params,
}: { }: {
params: { params: {
id: string; id: string;
}; };
}) { }) {
const router = useRouter(); const router = useRouter();
const [invitationStatus, setInvitationStatus] = useState< const [invitationStatus, setInvitationStatus] = useState<
"pending" | "accepted" | "rejected" "pending" | "accepted" | "rejected"
>("pending"); >("pending");
const handleAccept = async () => { const handleAccept = async () => {
await organization await organization
.acceptInvitation({ .acceptInvitation({
invitationId: params.id, invitationId: params.id,
}) })
.then((res) => { .then((res) => {
if (res.error) { if (res.error) {
setError(res.error.message || "An error occurred"); setError(res.error.message || "An error occurred");
} else { } else {
setInvitationStatus("accepted"); setInvitationStatus("accepted");
router.push(`/dashboard`); router.push(`/dashboard`);
} }
}); });
}; };
const handleReject = async () => { const handleReject = async () => {
await organization await organization
.rejectInvitation({ .rejectInvitation({
invitationId: params.id, invitationId: params.id,
}) })
.then((res) => { .then((res) => {
if (res.error) { if (res.error) {
setError(res.error.message || "An error occurred"); setError(res.error.message || "An error occurred");
} else { } else {
setInvitationStatus("rejected"); setInvitationStatus("rejected");
} }
}); });
}; };
const [invitation, setInvitation] = useState<{ const [invitation, setInvitation] = useState<{
organizationName: string; organizationName: string;
organizationSlug: string; organizationSlug: string;
inviterEmail: string; inviterEmail: string;
id: string; id: string;
status: "pending" | "accepted" | "rejected" | "canceled"; status: "pending" | "accepted" | "rejected" | "canceled";
email: string; email: string;
expiresAt: Date; expiresAt: Date;
organizationId: string; organizationId: string;
role: "member" | "admin" | "owner"; role: "member" | "admin" | "owner";
inviterId: string; inviterId: string;
} | null>(null); } | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
client.organization client.organization
.getInvitation({ .getInvitation({
query: { query: {
id: params.id, id: params.id,
}, },
}) })
.then((res) => { .then((res) => {
if (res.error) { if (res.error) {
setError(res.error.message || "An error occurred"); setError(res.error.message || "An error occurred");
} else { } else {
setInvitation(res.data); setInvitation(res.data);
} }
}); });
}, []); }, []);
return ( return (
<div className="min-h-[80vh] flex items-center justify-center"> <div className="min-h-[80vh] flex items-center justify-center">
<div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div> <div className="absolute pointer-events-none inset-0 flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]"></div>
{invitation ? ( {invitation ? (
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<CardHeader> <CardHeader>
<CardTitle>Organization Invitation</CardTitle> <CardTitle>Organization Invitation</CardTitle>
<CardDescription> <CardDescription>
You've been invited to join an organization You've been invited to join an organization
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{invitationStatus === "pending" && ( {invitationStatus === "pending" && (
<div className="space-y-4"> <div className="space-y-4">
<p> <p>
<strong>{invitation?.inviterEmail}</strong> has invited you to <strong>{invitation?.inviterEmail}</strong> has invited you to
join <strong>{invitation?.organizationName}</strong>. join <strong>{invitation?.organizationName}</strong>.
</p> </p>
<p> <p>
This invitation was sent to{" "} This invitation was sent to{" "}
<strong>{invitation?.email}</strong>. <strong>{invitation?.email}</strong>.
</p> </p>
</div> </div>
)} )}
{invitationStatus === "accepted" && ( {invitationStatus === "accepted" && (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-center w-16 h-16 mx-auto bg-green-100 rounded-full"> <div className="flex items-center justify-center w-16 h-16 mx-auto bg-green-100 rounded-full">
<CheckIcon className="w-8 h-8 text-green-600" /> <CheckIcon className="w-8 h-8 text-green-600" />
</div> </div>
<h2 className="text-2xl font-bold text-center"> <h2 className="text-2xl font-bold text-center">
Welcome to {invitation?.organizationName}! Welcome to {invitation?.organizationName}!
</h2> </h2>
<p className="text-center"> <p className="text-center">
You've successfully joined the organization. We're excited to You've successfully joined the organization. We're excited to
have you on board! have you on board!
</p> </p>
</div> </div>
)} )}
{invitationStatus === "rejected" && ( {invitationStatus === "rejected" && (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-center w-16 h-16 mx-auto bg-red-100 rounded-full"> <div className="flex items-center justify-center w-16 h-16 mx-auto bg-red-100 rounded-full">
<XIcon className="w-8 h-8 text-red-600" /> <XIcon className="w-8 h-8 text-red-600" />
</div> </div>
<h2 className="text-2xl font-bold text-center"> <h2 className="text-2xl font-bold text-center">
Invitation Declined Invitation Declined
</h2> </h2>
<p className="text-center"> <p className="text-center">
You&lsquo;ve declined the invitation to join{" "} You&lsquo;ve declined the invitation to join{" "}
{invitation?.organizationName}. {invitation?.organizationName}.
</p> </p>
</div> </div>
)} )}
</CardContent> </CardContent>
{invitationStatus === "pending" && ( {invitationStatus === "pending" && (
<CardFooter className="flex justify-between"> <CardFooter className="flex justify-between">
<Button variant="outline" onClick={handleReject}> <Button variant="outline" onClick={handleReject}>
Decline Decline
</Button> </Button>
<Button onClick={handleAccept}>Accept Invitation</Button> <Button onClick={handleAccept}>Accept Invitation</Button>
</CardFooter> </CardFooter>
)} )}
</Card> </Card>
) : error ? ( ) : error ? (
<InvitationError /> <InvitationError />
) : ( ) : (
<InvitationSkeleton /> <InvitationSkeleton />
)} )}
</div> </div>
); );
} }
function InvitationSkeleton() { function InvitationSkeleton() {
return ( return (
<Card className="w-full max-w-md mx-auto"> <Card className="w-full max-w-md mx-auto">
<CardHeader> <CardHeader>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Skeleton className="w-6 h-6 rounded-full" /> <Skeleton className="w-6 h-6 rounded-full" />
<Skeleton className="h-6 w-24" /> <Skeleton className="h-6 w-24" />
</div> </div>
<Skeleton className="h-4 w-full mt-2" /> <Skeleton className="h-4 w-full mt-2" />
<Skeleton className="h-4 w-2/3 mt-2" /> <Skeleton className="h-4 w-2/3 mt-2" />
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-2"> <div className="space-y-2">
<Skeleton className="h-4 w-full" /> <Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" /> <Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" /> <Skeleton className="h-4 w-2/3" />
</div> </div>
</CardContent> </CardContent>
<CardFooter className="flex justify-end"> <CardFooter className="flex justify-end">
<Skeleton className="h-10 w-24" /> <Skeleton className="h-10 w-24" />
</CardFooter> </CardFooter>
</Card> </Card>
); );
} }

View File

@@ -1,9 +1,9 @@
"use client"; "use client";
export function ManageAccount() { export function ManageAccount() {
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<p>Manage Account</p> <p>Manage Account</p>
</div> </div>
); );
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,25 +5,25 @@ import UserCard from "./user-card";
import { OrganizationCard } from "./organization-card"; import { OrganizationCard } from "./organization-card";
export default async function DashboardPage() { export default async function DashboardPage() {
const [session, activeSessions] = await Promise.all([ const [session, activeSessions] = await Promise.all([
auth.api.getSession({ auth.api.getSession({
headers: headers(), headers: headers(),
}), }),
auth.api.listSessions({ auth.api.listSessions({
headers: headers(), headers: headers(),
}), }),
]).catch((e) => { ]).catch((e) => {
throw redirect("/sign-in"); throw redirect("/sign-in");
}); });
return ( return (
<div className="w-full"> <div className="w-full">
<div className="flex gap-4 flex-col"> <div className="flex gap-4 flex-col">
<UserCard <UserCard
session={JSON.parse(JSON.stringify(session))} session={JSON.parse(JSON.stringify(session))}
activeSessions={JSON.parse(JSON.stringify(activeSessions))} activeSessions={JSON.parse(JSON.stringify(activeSessions))}
/> />
<OrganizationCard session={JSON.parse(JSON.stringify(session))} /> <OrganizationCard session={JSON.parse(JSON.stringify(session))} />
</div> </div>
</div> </div>
); );
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,95 +5,94 @@ import { AnimatePresence, motion } from "framer-motion";
import { Logo } from "@/components/logo"; import { Logo } from "@/components/logo";
export function Features() { export function Features() {
return ( return (
<> <>
<div className="flex flex-col lg:flex-row bg-white dark:bg-black w-full gap-4 mx-auto px-8"> <div className="flex flex-col lg:flex-row bg-white dark:bg-black w-full gap-4 mx-auto px-8">
<Card title="BetterAuth" icon={<Logo className=" w-44" />}> <Card title="BetterAuth" icon={<Logo className=" w-44" />}></Card>
</Card> </div>
</div> </>
</> );
);
} }
const Card = ({ const Card = ({
title, title,
icon, icon,
children, children,
}: { }: {
title: string; title: string;
icon: React.ReactNode; icon: React.ReactNode;
children?: React.ReactNode; children?: React.ReactNode;
}) => { }) => {
const [hovered, setHovered] = React.useState(false); const [hovered, setHovered] = React.useState(false);
return ( return (
<div <div
onMouseEnter={() => setHovered(true)} onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
className="border border-black/[0.2] group/canvas-card flex items-center justify-center dark:border-white/[0.2] max-w-sm w-full mx-auto p-4 relative h-[18rem]" className="border border-black/[0.2] group/canvas-card flex items-center justify-center dark:border-white/[0.2] max-w-sm w-full mx-auto p-4 relative h-[18rem]"
> >
<Icon className="absolute h-6 w-6 -top-3 -left-3 dark:text-white text-black" /> <Icon className="absolute h-6 w-6 -top-3 -left-3 dark:text-white text-black" />
<Icon className="absolute h-6 w-6 -bottom-3 -left-3 dark:text-white text-black" /> <Icon className="absolute h-6 w-6 -bottom-3 -left-3 dark:text-white text-black" />
<Icon className="absolute h-6 w-6 -top-3 -right-3 dark:text-white text-black" /> <Icon className="absolute h-6 w-6 -top-3 -right-3 dark:text-white text-black" />
<Icon className="absolute h-6 w-6 -bottom-3 -right-3 dark:text-white text-black" /> <Icon className="absolute h-6 w-6 -bottom-3 -right-3 dark:text-white text-black" />
<AnimatePresence> <AnimatePresence>
{hovered && ( {hovered && (
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
className="h-full w-full absolute inset-0" className="h-full w-full absolute inset-0"
> >
{children} {children}
</motion.div> </motion.div>
)} )}
</AnimatePresence> </AnimatePresence>
<div className="relative z-20"> <div className="relative z-20">
<div className="text-center group-hover/canvas-card:-translate-y-4 group-hover/canvas-card:opacity-0 transition duration-200 w-full mx-auto flex items-center justify-center"> <div className="text-center group-hover/canvas-card:-translate-y-4 group-hover/canvas-card:opacity-0 transition duration-200 w-full mx-auto flex items-center justify-center">
{icon} {icon}
</div> </div>
<h2 className="dark:text-white text-xl opacity-0 group-hover/canvas-card:opacity-100 relative z-10 text-black mt-4 font-bold group-hover/canvas-card:text-white group-hover/canvas-card:-translate-y-2 transition duration-200"> <h2 className="dark:text-white text-xl opacity-0 group-hover/canvas-card:opacity-100 relative z-10 text-black mt-4 font-bold group-hover/canvas-card:text-white group-hover/canvas-card:-translate-y-2 transition duration-200">
{title} {title}
</h2> </h2>
</div> </div>
</div> </div>
); );
}; };
const AceternityIcon = () => { const AceternityIcon = () => {
return ( return (
<svg <svg
width="66" width="66"
height="65" height="65"
viewBox="0 0 66 65" viewBox="0 0 66 65"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="h-10 w-10 text-black dark:text-white group-hover/canvas-card:text-white " className="h-10 w-10 text-black dark:text-white group-hover/canvas-card:text-white "
> >
<path <path
d="M8 8.05571C8 8.05571 54.9009 18.1782 57.8687 30.062C60.8365 41.9458 9.05432 57.4696 9.05432 57.4696" d="M8 8.05571C8 8.05571 54.9009 18.1782 57.8687 30.062C60.8365 41.9458 9.05432 57.4696 9.05432 57.4696"
stroke="currentColor" stroke="currentColor"
strokeWidth="15" strokeWidth="15"
strokeMiterlimit="3.86874" strokeMiterlimit="3.86874"
strokeLinecap="round" strokeLinecap="round"
style={{ mixBlendMode: "darken" }} style={{ mixBlendMode: "darken" }}
/> />
</svg> </svg>
); );
}; };
export const Icon = ({ className, ...rest }: any) => { export const Icon = ({ className, ...rest }: any) => {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
strokeWidth="1.5" strokeWidth="1.5"
stroke="currentColor" stroke="currentColor"
className={className} className={className}
{...rest} {...rest}
> >
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" /> <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" />
</svg> </svg>
); );
}; };

View File

@@ -9,32 +9,32 @@ import { Wrapper, WrapperWithQuery } from "@/components/wrapper";
import { createMetadata } from "@/lib/metadata"; import { createMetadata } from "@/lib/metadata";
export const metadata = createMetadata({ export const metadata = createMetadata({
title: { title: {
template: "%s | Better Auth", template: "%s | Better Auth",
default: "Better Auth", default: "Better Auth",
}, },
description: "The most comprehensive authentication library for typescript", description: "The most comprehensive authentication library for typescript",
metadataBase: new URL("https://demo.better-auth.com"), metadataBase: new URL("https://demo.better-auth.com"),
}); });
export default function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<head> <head>
<link rel="icon" href="/favicon/favicon.ico" sizes="any" /> <link rel="icon" href="/favicon/favicon.ico" sizes="any" />
</head> </head>
<body className={`${GeistSans.variable} ${GeistMono.variable} font-sans`}> <body className={`${GeistSans.variable} ${GeistMono.variable} font-sans`}>
<ThemeProvider attribute="class" defaultTheme="dark"> <ThemeProvider attribute="class" defaultTheme="dark">
<Wrapper> <Wrapper>
<WrapperWithQuery>{children}</WrapperWithQuery> <WrapperWithQuery>{children}</WrapperWithQuery>
</Wrapper> </Wrapper>
<Toaster richColors closeButton /> <Toaster richColors closeButton />
</ThemeProvider> </ThemeProvider>
</body> </body>
</html> </html>
); );
} }

View File

@@ -3,61 +3,61 @@ import { headers } from "next/headers";
import { Suspense } from "react"; import { Suspense } from "react";
export default async function Home() { export default async function Home() {
const features = [ const features = [
"Email & Password", "Email & Password",
"Organization | Teams", "Organization | Teams",
"Passkeys", "Passkeys",
"Multi Factor", "Multi Factor",
"Password Reset", "Password Reset",
"Roles & Permissions", "Roles & Permissions",
"Rate Limiting", "Rate Limiting",
"Session Management", "Session Management",
]; ];
return ( return (
<div className="min-h-[80vh] flex items-center justify-center overflow-hidden no-visible-scrollbar px-6 md:px-0"> <div className="min-h-[80vh] flex items-center justify-center overflow-hidden no-visible-scrollbar px-6 md:px-0">
<main className="flex flex-col gap-4 row-start-2 items-center justify-center"> <main className="flex flex-col gap-4 row-start-2 items-center justify-center">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<h3 className="font-bold text-4xl text-black dark:text-white text-center"> <h3 className="font-bold text-4xl text-black dark:text-white text-center">
Better Auth. Better Auth.
</h3> </h3>
<p className="text-center break-words text-sm md:text-base"> <p className="text-center break-words text-sm md:text-base">
Official demo to showcase{" "} Official demo to showcase{" "}
<a <a
href="https://better-auth.com" href="https://better-auth.com"
target="_blank" target="_blank"
className="italic underline" className="italic underline"
> >
better-auth. better-auth.
</a>{" "} </a>{" "}
features and capabilities. <br /> features and capabilities. <br />
</p> </p>
</div> </div>
<div className="md:w-10/12 w-full flex flex-col gap-4"> <div className="md:w-10/12 w-full flex flex-col gap-4">
<div className="flex flex-col gap-3 pt-2 flex-wrap"> <div className="flex flex-col gap-3 pt-2 flex-wrap">
<div className="border-y py-2 border-dotted bg-secondary/60 opacity-80"> <div className="border-y py-2 border-dotted bg-secondary/60 opacity-80">
<div className="text-xs flex items-center gap-2 justify-center text-muted-foreground "> <div className="text-xs flex items-center gap-2 justify-center text-muted-foreground ">
<span className="text-center"> <span className="text-center">
All features on this demo are Implemented with better auth All features on this demo are Implemented with better auth
without any custom backend code without any custom backend code
</span> </span>
</div> </div>
</div> </div>
<div className="flex gap-2 justify-center flex-wrap"> <div className="flex gap-2 justify-center flex-wrap">
{features.map((feature) => ( {features.map((feature) => (
<span <span
className="border-b pb-1 text-muted-foreground text-xs cursor-pointer hover:text-foreground duration-150 ease-in-out transition-all hover:border-foreground flex items-center gap-1" className="border-b pb-1 text-muted-foreground text-xs cursor-pointer hover:text-foreground duration-150 ease-in-out transition-all hover:border-foreground flex items-center gap-1"
key={feature} key={feature}
> >
{feature}. {feature}.
</span> </span>
))} ))}
</div> </div>
</div> </div>
<Suspense fallback={<SignInFallback />}> <Suspense fallback={<SignInFallback />}>
<SignInButton /> <SignInButton />
</Suspense> </Suspense>
</div> </div>
</main> </main>
</div> </div>
); );
} }

View File

@@ -1,20 +1,20 @@
{ {
"$schema": "https://ui.shadcn.com/schema.json", "$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york", "style": "new-york",
"rsc": true, "rsc": true,
"tsx": true, "tsx": true,
"tailwind": { "tailwind": {
"config": "tailwind.config.ts", "config": "tailwind.config.ts",
"css": "app/globals.css", "css": "app/globals.css",
"baseColor": "neutral", "baseColor": "neutral",
"cssVariables": true, "cssVariables": true,
"prefix": "" "prefix": ""
}, },
"aliases": { "aliases": {
"components": "@/components", "components": "@/components",
"utils": "@/lib/utils", "utils": "@/lib/utils",
"ui": "@/components/ui", "ui": "@/components/ui",
"lib": "@/lib", "lib": "@/lib",
"hooks": "@/hooks" "hooks": "@/hooks"
} }
} }

View File

@@ -1,24 +1,24 @@
import { SVGProps } from "react"; import { SVGProps } from "react";
export const Logo = (props: SVGProps<any>) => { export const Logo = (props: SVGProps<any>) => {
return ( return (
<svg <svg
width="22" width="22"
height="22" height="22"
viewBox="0 0 200 200" viewBox="0 0 200 200"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
{...props} {...props}
> >
<rect width="200" height="200" fill="#D9D9D9" /> <rect width="200" height="200" fill="#D9D9D9" />
<line x1="21.5" y1="2.18557e-08" x2="21.5" y2="200" stroke="#C4C4C4" /> <line x1="21.5" y1="2.18557e-08" x2="21.5" y2="200" stroke="#C4C4C4" />
<line x1="173.5" y1="2.18557e-08" x2="173.5" y2="200" stroke="#C4C4C4" /> <line x1="173.5" y1="2.18557e-08" x2="173.5" y2="200" stroke="#C4C4C4" />
<line x1="200" y1="176.5" x2="-4.37114e-08" y2="176.5" stroke="#C4C4C4" /> <line x1="200" y1="176.5" x2="-4.37114e-08" y2="176.5" stroke="#C4C4C4" />
<line x1="200" y1="24.5" x2="-4.37114e-08" y2="24.5" stroke="#C4C4C4" /> <line x1="200" y1="24.5" x2="-4.37114e-08" y2="24.5" stroke="#C4C4C4" />
<path <path
d="M64.4545 135V65.1818H88.8636C93.7273 65.1818 97.7386 66.0227 100.898 67.7045C104.057 69.3636 106.409 71.6023 107.955 74.4205C109.5 77.2159 110.273 80.3182 110.273 83.7273C110.273 86.7273 109.739 89.2045 108.67 91.1591C107.625 93.1136 106.239 94.6591 104.511 95.7955C102.807 96.9318 100.955 97.7727 98.9545 98.3182V99C101.091 99.1364 103.239 99.8864 105.398 101.25C107.557 102.614 109.364 104.568 110.818 107.114C112.273 109.659 113 112.773 113 116.455C113 119.955 112.205 123.102 110.614 125.898C109.023 128.693 106.511 130.909 103.08 132.545C99.6477 134.182 95.1818 135 89.6818 135H64.4545ZM72.9091 127.5H89.6818C95.2045 127.5 99.125 126.432 101.443 124.295C103.784 122.136 104.955 119.523 104.955 116.455C104.955 114.091 104.352 111.909 103.148 109.909C101.943 107.886 100.227 106.273 98 105.068C95.7727 103.841 93.1364 103.227 90.0909 103.227H72.9091V127.5ZM72.9091 95.8636H88.5909C91.1364 95.8636 93.4318 95.3636 95.4773 94.3636C97.5455 93.3636 99.1818 91.9545 100.386 90.1364C101.614 88.3182 102.227 86.1818 102.227 83.7273C102.227 80.6591 101.159 78.0568 99.0227 75.9205C96.8864 73.7614 93.5 72.6818 88.8636 72.6818H72.9091V95.8636ZM131.665 135.545C129.983 135.545 128.54 134.943 127.335 133.739C126.131 132.534 125.528 131.091 125.528 129.409C125.528 127.727 126.131 126.284 127.335 125.08C128.54 123.875 129.983 123.273 131.665 123.273C133.347 123.273 134.79 123.875 135.994 125.08C137.199 126.284 137.801 127.727 137.801 129.409C137.801 130.523 137.517 131.545 136.949 132.477C136.403 133.409 135.665 134.159 134.733 134.727C133.824 135.273 132.801 135.545 131.665 135.545Z" d="M64.4545 135V65.1818H88.8636C93.7273 65.1818 97.7386 66.0227 100.898 67.7045C104.057 69.3636 106.409 71.6023 107.955 74.4205C109.5 77.2159 110.273 80.3182 110.273 83.7273C110.273 86.7273 109.739 89.2045 108.67 91.1591C107.625 93.1136 106.239 94.6591 104.511 95.7955C102.807 96.9318 100.955 97.7727 98.9545 98.3182V99C101.091 99.1364 103.239 99.8864 105.398 101.25C107.557 102.614 109.364 104.568 110.818 107.114C112.273 109.659 113 112.773 113 116.455C113 119.955 112.205 123.102 110.614 125.898C109.023 128.693 106.511 130.909 103.08 132.545C99.6477 134.182 95.1818 135 89.6818 135H64.4545ZM72.9091 127.5H89.6818C95.2045 127.5 99.125 126.432 101.443 124.295C103.784 122.136 104.955 119.523 104.955 116.455C104.955 114.091 104.352 111.909 103.148 109.909C101.943 107.886 100.227 106.273 98 105.068C95.7727 103.841 93.1364 103.227 90.0909 103.227H72.9091V127.5ZM72.9091 95.8636H88.5909C91.1364 95.8636 93.4318 95.3636 95.4773 94.3636C97.5455 93.3636 99.1818 91.9545 100.386 90.1364C101.614 88.3182 102.227 86.1818 102.227 83.7273C102.227 80.6591 101.159 78.0568 99.0227 75.9205C96.8864 73.7614 93.5 72.6818 88.8636 72.6818H72.9091V95.8636ZM131.665 135.545C129.983 135.545 128.54 134.943 127.335 133.739C126.131 132.534 125.528 131.091 125.528 129.409C125.528 127.727 126.131 126.284 127.335 125.08C128.54 123.875 129.983 123.273 131.665 123.273C133.347 123.273 134.79 123.875 135.994 125.08C137.199 126.284 137.801 127.727 137.801 129.409C137.801 130.523 137.517 131.545 136.949 132.477C136.403 133.409 135.665 134.159 134.733 134.727C133.824 135.273 132.801 135.545 131.665 135.545Z"
fill="#302208" fill="#302208"
/> />
</svg> </svg>
); );
}; };

View File

@@ -4,62 +4,84 @@ import { auth } from "@/lib/auth";
import { headers } from "next/headers"; import { headers } from "next/headers";
export async function SignInButton() { export async function SignInButton() {
const session = await auth.api.getSession({ const session = await auth.api.getSession({
headers: headers() headers: headers(),
}) });
return ( return (
<Link href={ <Link
session?.session ? "/dashboard" : "/sign-in" href={session?.session ? "/dashboard" : "/sign-in"}
} className="flex justify-center"> className="flex justify-center"
<Button className="gap-2 justify-between" variant="default"> >
<Button className="gap-2 justify-between" variant="default">
{ {!session?.session ? (
!session?.session ? ( <svg
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24"><path fill="currentColor" d="M5 3H3v4h2V5h14v14H5v-2H3v4h18V3zm12 8h-2V9h-2V7h-2v2h2v2H3v2h10v2h-2v2h2v-2h2v-2h2z"></path></svg> xmlns="http://www.w3.org/2000/svg"
) : ( width="1.2em"
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24"><path fill="currentColor" d="M2 3h20v18H2zm18 16V7H4v12z"></path></svg> height="1.2em"
) viewBox="0 0 24 24"
} >
<span> <path
{ fill="currentColor"
session?.session ? "Dashboard" : "Sign In" d="M5 3H3v4h2V5h14v14H5v-2H3v4h18V3zm12 8h-2V9h-2V7h-2v2h2v2H3v2h10v2h-2v2h2v-2h2v-2h2z"
} ></path>
</span> </svg>
</Button> ) : (
</Link> <svg
) xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path fill="currentColor" d="M2 3h20v18H2zm18 16V7H4v12z"></path>
</svg>
)}
<span>{session?.session ? "Dashboard" : "Sign In"}</span>
</Button>
</Link>
);
} }
function checkOptimisticSession(headers: Headers) { function checkOptimisticSession(headers: Headers) {
const guessIsSignIn = headers.get("cookie")?.includes("better-auth.session") || headers.get("cookie")?.includes("__Secure-better-auth.session-token") const guessIsSignIn =
return !!guessIsSignIn; headers.get("cookie")?.includes("better-auth.session") ||
headers.get("cookie")?.includes("__Secure-better-auth.session-token");
return !!guessIsSignIn;
} }
export function SignInFallback() { export function SignInFallback() {
//to avoid flash of unauthenticated state //to avoid flash of unauthenticated state
const guessIsSignIn = checkOptimisticSession(headers()) const guessIsSignIn = checkOptimisticSession(headers());
return ( return (
<Link href={ <Link
guessIsSignIn ? "/dashboard" : "/sign-in" href={guessIsSignIn ? "/dashboard" : "/sign-in"}
} className="flex justify-center"> className="flex justify-center"
<Button className="gap-2 justify-between" variant="default"> >
<Button className="gap-2 justify-between" variant="default">
{ {!guessIsSignIn ? (
!guessIsSignIn ? ( <svg
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24"><path fill="currentColor" d="M5 3H3v4h2V5h14v14H5v-2H3v4h18V3zm12 8h-2V9h-2V7h-2v2h2v2H3v2h10v2h-2v2h2v-2h2v-2h2z"></path></svg> xmlns="http://www.w3.org/2000/svg"
) : ( width="1.2em"
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24"><path fill="currentColor" d="M2 3h20v18H2zm18 16V7H4v12z"></path></svg> height="1.2em"
) viewBox="0 0 24 24"
} >
<span> <path
{ fill="currentColor"
guessIsSignIn ? "Dashboard" : "Sign In" d="M5 3H3v4h2V5h14v14H5v-2H3v4h18V3zm12 8h-2V9h-2V7h-2v2h2v2H3v2h10v2h-2v2h2v-2h2v-2h2z"
} ></path>
</span> </svg>
</Button> ) : (
</Link> <svg
) xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path fill="currentColor" d="M2 3h20v18H2zm18 16V7H4v12z"></path>
</svg>
)}
<span>{guessIsSignIn ? "Dashboard" : "Sign In"}</span>
</Button>
</Link>
);
} }

View File

@@ -2,12 +2,12 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -22,156 +22,156 @@ import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
export default function SignIn() { export default function SignIn() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
return ( return (
<Card className="z-50 rounded-md rounded-t-none max-w-md"> <Card className="z-50 rounded-md rounded-t-none max-w-md">
<CardHeader> <CardHeader>
<CardTitle className="text-lg md:text-xl">Sign In</CardTitle> <CardTitle className="text-lg md:text-xl">Sign In</CardTitle>
<CardDescription className="text-xs md:text-sm"> <CardDescription className="text-xs md:text-sm">
Enter your email below to login to your account Enter your email below to login to your account
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid gap-4"> <div className="grid gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email">Email</Label> <Label htmlFor="email">Email</Label>
<Input <Input
id="email" id="email"
type="email" type="email"
placeholder="m@example.com" placeholder="m@example.com"
required required
onChange={(e) => { onChange={(e) => {
setEmail(e.target.value); setEmail(e.target.value);
}} }}
value={email} value={email}
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<div className="flex items-center"> <div className="flex items-center">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
<Link <Link
href="/forget-password" href="/forget-password"
className="ml-auto inline-block text-sm underline" className="ml-auto inline-block text-sm underline"
> >
Forgot your password? Forgot your password?
</Link> </Link>
</div> </div>
<PasswordInput <PasswordInput
id="password" id="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
autoComplete="password" autoComplete="password"
placeholder="Password" placeholder="Password"
/> />
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Checkbox <Checkbox
onClick={() => { onClick={() => {
setRememberMe(!rememberMe); setRememberMe(!rememberMe);
}} }}
/> />
<Label>Remember me</Label> <Label>Remember me</Label>
</div> </div>
<Button <Button
type="submit" type="submit"
className="w-full" className="w-full"
disabled={loading} disabled={loading}
onClick={async () => { onClick={async () => {
await signIn.email({ await signIn.email({
email: email, email: email,
password: password, password: password,
callbackURL: "/dashboard", callbackURL: "/dashboard",
dontRememberMe: !rememberMe, dontRememberMe: !rememberMe,
fetchOptions: { fetchOptions: {
onRequest: () => { onRequest: () => {
setLoading(true); setLoading(true);
}, },
onResponse: () => { onResponse: () => {
setLoading(false); setLoading(false);
}, },
onError: (ctx) => { onError: (ctx) => {
toast.error(ctx.error.message); toast.error(ctx.error.message);
}, },
}, },
}); });
}} }}
> >
{loading ? <Loader2 size={16} className="animate-spin" /> : "Login"} {loading ? <Loader2 size={16} className="animate-spin" /> : "Login"}
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="w-full gap-2" className="w-full gap-2"
onClick={async () => { onClick={async () => {
await signIn.social({ await signIn.social({
provider: "github", provider: "github",
callbackURL: "/dashboard", callbackURL: "/dashboard",
}); });
}} }}
> >
<GitHubLogoIcon /> <GitHubLogoIcon />
Continue with Github Continue with Github
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="w-full gap-2" className="w-full gap-2"
onClick={async () => { onClick={async () => {
await signIn.social({ await signIn.social({
provider: "google", provider: "google",
callbackURL: "/dashboard", callbackURL: "/dashboard",
}); });
}} }}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="0.98em" width="0.98em"
height="1em" height="1em"
viewBox="0 0 256 262" viewBox="0 0 256 262"
> >
<path <path
fill="#4285F4" fill="#4285F4"
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
/> />
<path <path
fill="#34A853" fill="#34A853"
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
/> />
<path <path
fill="#FBBC05" fill="#FBBC05"
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z" d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
/> />
<path <path
fill="#EB4335" fill="#EB4335"
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
/> />
</svg> </svg>
Continue with Google Continue with Google
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="gap-2" className="gap-2"
onClick={async () => { onClick={async () => {
await signIn.passkey({ await signIn.passkey({
callbackURL: "/dashboard", callbackURL: "/dashboard",
}); });
}} }}
> >
<Key size={16} /> <Key size={16} />
Sign-in with Passkey Sign-in with Passkey
</Button> </Button>
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<div className="flex justify-center w-full border-t py-4"> <div className="flex justify-center w-full border-t py-4">
<p className="text-center text-xs text-neutral-500"> <p className="text-center text-xs text-neutral-500">
Secured by <span className="text-orange-400">better-auth.</span> Secured by <span className="text-orange-400">better-auth.</span>
</p> </p>
</div> </div>
</CardFooter> </CardFooter>
</Card> </Card>
); );
} }

View File

@@ -2,12 +2,12 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -20,244 +20,244 @@ import { Loader2, X } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
export function SignUp() { export function SignUp() {
const [firstName, setFirstName] = useState(""); const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState(""); const [lastName, setLastName] = useState("");
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [passwordConfirmation, setPasswordConfirmation] = useState(""); const [passwordConfirmation, setPasswordConfirmation] = useState("");
const [image, setImage] = useState<File | null>(null); const [image, setImage] = useState<File | null>(null);
const [imagePreview, setImagePreview] = useState<string | null>(null); const [imagePreview, setImagePreview] = useState<string | null>(null);
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (file) { if (file) {
setImage(file); setImage(file);
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = () => { reader.onloadend = () => {
setImagePreview(reader.result as string); setImagePreview(reader.result as string);
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
}; };
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
return ( return (
<Card className="z-50 rounded-md rounded-t-none max-w-md"> <Card className="z-50 rounded-md rounded-t-none max-w-md">
<CardHeader> <CardHeader>
<CardTitle className="text-lg md:text-xl">Sign Up</CardTitle> <CardTitle className="text-lg md:text-xl">Sign Up</CardTitle>
<CardDescription className="text-xs md:text-sm"> <CardDescription className="text-xs md:text-sm">
Enter your information to create an account Enter your information to create an account
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid gap-4"> <div className="grid gap-4">
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="first-name">First name</Label> <Label htmlFor="first-name">First name</Label>
<Input <Input
id="first-name" id="first-name"
placeholder="Max" placeholder="Max"
required required
onChange={(e) => { onChange={(e) => {
setFirstName(e.target.value); setFirstName(e.target.value);
}} }}
value={firstName} value={firstName}
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="last-name">Last name</Label> <Label htmlFor="last-name">Last name</Label>
<Input <Input
id="last-name" id="last-name"
placeholder="Robinson" placeholder="Robinson"
required required
onChange={(e) => { onChange={(e) => {
setLastName(e.target.value); setLastName(e.target.value);
}} }}
value={lastName} value={lastName}
/> />
</div> </div>
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email">Email</Label> <Label htmlFor="email">Email</Label>
<Input <Input
id="email" id="email"
type="email" type="email"
placeholder="m@example.com" placeholder="m@example.com"
required required
onChange={(e) => { onChange={(e) => {
setEmail(e.target.value); setEmail(e.target.value);
}} }}
value={email} value={email}
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
<PasswordInput <PasswordInput
id="password" id="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
autoComplete="new-password" autoComplete="new-password"
placeholder="Password" placeholder="Password"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="password">Confirm Password</Label> <Label htmlFor="password">Confirm Password</Label>
<PasswordInput <PasswordInput
id="password_confirmation" id="password_confirmation"
value={passwordConfirmation} value={passwordConfirmation}
onChange={(e) => setPasswordConfirmation(e.target.value)} onChange={(e) => setPasswordConfirmation(e.target.value)}
autoComplete="new-password" autoComplete="new-password"
placeholder="Confirm Password" placeholder="Confirm Password"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="image">Profile Image (optional)</Label> <Label htmlFor="image">Profile Image (optional)</Label>
<div className="flex items-end gap-4"> <div className="flex items-end gap-4">
{imagePreview && ( {imagePreview && (
<div className="relative w-16 h-16 rounded-sm overflow-hidden"> <div className="relative w-16 h-16 rounded-sm overflow-hidden">
<Image <Image
src={imagePreview} src={imagePreview}
alt="Profile preview" alt="Profile preview"
layout="fill" layout="fill"
objectFit="cover" objectFit="cover"
/> />
</div> </div>
)} )}
<div className="flex items-center gap-2 w-full"> <div className="flex items-center gap-2 w-full">
<Input <Input
id="image" id="image"
type="file" type="file"
accept="image/*" accept="image/*"
onChange={handleImageChange} onChange={handleImageChange}
className="w-full" className="w-full"
/> />
{imagePreview && ( {imagePreview && (
<X <X
className="cursor-pointer" className="cursor-pointer"
onClick={() => { onClick={() => {
setImage(null); setImage(null);
setImagePreview(null); setImagePreview(null);
}} }}
/> />
)} )}
</div> </div>
</div> </div>
</div> </div>
<Button <Button
type="submit" type="submit"
className="w-full" className="w-full"
disabled={loading} disabled={loading}
onClick={async () => { onClick={async () => {
await signUp.email({ await signUp.email({
email, email,
password, password,
name: `${firstName} ${lastName}`, name: `${firstName} ${lastName}`,
image: image ? await convertImageToBase64(image) : "", image: image ? await convertImageToBase64(image) : "",
callbackURL: "/dashboard", callbackURL: "/dashboard",
fetchOptions: { fetchOptions: {
onResponse: () => { onResponse: () => {
setLoading(false); setLoading(false);
}, },
onRequest: () => { onRequest: () => {
setLoading(true); setLoading(true);
}, },
onError: (ctx) => { onError: (ctx) => {
toast.error(ctx.error.message); toast.error(ctx.error.message);
}, },
}, },
}); });
}} }}
> >
{loading ? ( {loading ? (
<Loader2 size={16} className="animate-spin" /> <Loader2 size={16} className="animate-spin" />
) : ( ) : (
"Create an account" "Create an account"
)} )}
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="w-full gap-2" className="w-full gap-2"
onClick={async () => { onClick={async () => {
const res = await client.signIn.social({ const res = await client.signIn.social({
provider: "google", provider: "google",
callbackURL: "/dashboard", callbackURL: "/dashboard",
fetchOptions: { fetchOptions: {
onRequest: () => { onRequest: () => {
setLoading(true); setLoading(true);
}, },
onResponse: () => { onResponse: () => {
setLoading(false); setLoading(false);
}, },
}, },
}); });
}} }}
disabled={loading} disabled={loading}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="0.98em" width="0.98em"
height="1em" height="1em"
viewBox="0 0 256 262" viewBox="0 0 256 262"
> >
<path <path
fill="#4285F4" fill="#4285F4"
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
/> />
<path <path
fill="#34A853" fill="#34A853"
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
/> />
<path <path
fill="#FBBC05" fill="#FBBC05"
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z" d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
/> />
<path <path
fill="#EB4335" fill="#EB4335"
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
/> />
</svg> </svg>
Continue with Google Continue with Google
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="w-full gap-2" className="w-full gap-2"
onClick={async () => { onClick={async () => {
await signIn.social({ await signIn.social({
provider: "github", provider: "github",
callbackURL: "/dashboard", callbackURL: "/dashboard",
fetchOptions: { fetchOptions: {
onRequest: () => { onRequest: () => {
setLoading(true); setLoading(true);
}, },
onResponse: () => { onResponse: () => {
setLoading(false); setLoading(false);
}, },
}, },
}); });
}} }}
disabled={loading} disabled={loading}
> >
<GitHubLogoIcon /> <GitHubLogoIcon />
Continue with Github Continue with Github
</Button> </Button>
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<div className="flex justify-center w-full border-t py-4"> <div className="flex justify-center w-full border-t py-4">
<p className="text-center text-xs text-neutral-500"> <p className="text-center text-xs text-neutral-500">
Secured by <span className="text-orange-400">better-auth.</span> Secured by <span className="text-orange-400">better-auth.</span>
</p> </p>
</div> </div>
</CardFooter> </CardFooter>
</Card> </Card>
); );
} }
async function convertImageToBase64(file: File): Promise<string> { async function convertImageToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
reader.onloadend = () => resolve(reader.result as string); reader.onloadend = () => resolve(reader.result as string);
reader.onerror = reject; reader.onerror = reject;
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
} }

View File

@@ -5,5 +5,5 @@ import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types"; import { type ThemeProviderProps } from "next-themes/dist/types";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) { export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>; return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
} }

View File

@@ -7,17 +7,17 @@ import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
export function ThemeToggle() { export function ThemeToggle() {
const { setTheme, theme } = useTheme(); const { setTheme, theme } = useTheme();
return ( return (
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")} onClick={() => setTheme(theme === "light" ? "dark" : "light")}
> >
<Sun className="h-[1.5rem] w-[1.3rem] dark:hidden" color="#000" /> <Sun className="h-[1.5rem] w-[1.3rem] dark:hidden" color="#000" />
<Moon className="hidden h-5 w-5 dark:block" /> <Moon className="hidden h-5 w-5 dark:block" />
<span className="sr-only">Toggle theme</span> <span className="sr-only">Toggle theme</span>
</Button> </Button>
); );
} }

View File

@@ -1,57 +1,57 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion" import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "@radix-ui/react-icons" import { ChevronDownIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Accordion = AccordionPrimitive.Root const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef< const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>, React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AccordionPrimitive.Item <AccordionPrimitive.Item
ref={ref} ref={ref}
className={cn("border-b", className)} className={cn("border-b", className)}
{...props} {...props}
/> />
)) ));
AccordionItem.displayName = "AccordionItem" AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef< const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>, React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180", "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" /> <ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
)) ));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef< const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>, React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content <AccordionPrimitive.Content
ref={ref} ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props} {...props}
> >
<div className={cn("pb-4 pt-0", className)}>{children}</div> <div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content> </AccordionPrimitive.Content>
)) ));
AccordionContent.displayName = AccordionPrimitive.Content.displayName AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@@ -1,141 +1,141 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button" import { buttonVariants } from "@/components/ui/button";
const AlertDialog = AlertDialogPrimitive.Root const AlertDialog = AlertDialogPrimitive.Root;
const AlertDialogTrigger = AlertDialogPrimitive.Trigger const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
const AlertDialogPortal = AlertDialogPrimitive.Portal const AlertDialogPortal = AlertDialogPrimitive.Portal;
const AlertDialogOverlay = React.forwardRef< const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>, React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
/> />
)) ));
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
const AlertDialogContent = React.forwardRef< const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>, React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay /> <AlertDialogOverlay />
<AlertDialogPrimitive.Content <AlertDialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className className,
)} )}
{...props} {...props}
/> />
</AlertDialogPortal> </AlertDialogPortal>
)) ));
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
const AlertDialogHeader = ({ const AlertDialogHeader = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col space-y-2 text-center sm:text-left", "flex flex-col space-y-2 text-center sm:text-left",
className className,
)} )}
{...props} {...props}
/> />
) );
AlertDialogHeader.displayName = "AlertDialogHeader" AlertDialogHeader.displayName = "AlertDialogHeader";
const AlertDialogFooter = ({ const AlertDialogFooter = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
) );
AlertDialogFooter.displayName = "AlertDialogFooter" AlertDialogFooter.displayName = "AlertDialogFooter";
const AlertDialogTitle = React.forwardRef< const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>, React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title <AlertDialogPrimitive.Title
ref={ref} ref={ref}
className={cn("text-lg font-semibold", className)} className={cn("text-lg font-semibold", className)}
{...props} {...props}
/> />
)) ));
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
const AlertDialogDescription = React.forwardRef< const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>, React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description <AlertDialogPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
AlertDialogDescription.displayName = AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName AlertDialogPrimitive.Description.displayName;
const AlertDialogAction = React.forwardRef< const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>, React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action <AlertDialogPrimitive.Action
ref={ref} ref={ref}
className={cn(buttonVariants(), className)} className={cn(buttonVariants(), className)}
{...props} {...props}
/> />
)) ));
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
const AlertDialogCancel = React.forwardRef< const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>, React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel <AlertDialogPrimitive.Cancel
ref={ref} ref={ref}
className={cn( className={cn(
buttonVariants({ variant: "outline" }), buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0", "mt-2 sm:mt-0",
className className,
)} )}
{...props} {...props}
/> />
)) ));
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
export { export {
AlertDialog, AlertDialog,
AlertDialogPortal, AlertDialogPortal,
AlertDialogOverlay, AlertDialogOverlay,
AlertDialogTrigger, AlertDialogTrigger,
AlertDialogContent, AlertDialogContent,
AlertDialogHeader, AlertDialogHeader,
AlertDialogFooter, AlertDialogFooter,
AlertDialogTitle, AlertDialogTitle,
AlertDialogDescription, AlertDialogDescription,
AlertDialogAction, AlertDialogAction,
AlertDialogCancel, AlertDialogCancel,
} };

View File

@@ -1,59 +1,59 @@
import * as React from "react" import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const alertVariants = cva( const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-background text-foreground", default: "bg-background text-foreground",
destructive: destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
const Alert = React.forwardRef< const Alert = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => ( >(({ className, variant, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
role="alert" role="alert"
className={cn(alertVariants({ variant }), className)} className={cn(alertVariants({ variant }), className)}
{...props} {...props}
/> />
)) ));
Alert.displayName = "Alert" Alert.displayName = "Alert";
const AlertTitle = React.forwardRef< const AlertTitle = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement> React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<h5 <h5
ref={ref} ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)} className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props} {...props}
/> />
)) ));
AlertTitle.displayName = "AlertTitle" AlertTitle.displayName = "AlertTitle";
const AlertDescription = React.forwardRef< const AlertDescription = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement> React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)} className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props} {...props}
/> />
)) ));
AlertDescription.displayName = "AlertDescription" AlertDescription.displayName = "AlertDescription";
export { Alert, AlertTitle, AlertDescription } export { Alert, AlertTitle, AlertDescription };

View File

@@ -1,7 +1,7 @@
"use client" "use client";
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
const AspectRatio = AspectRatioPrimitive.Root const AspectRatio = AspectRatioPrimitive.Root;
export { AspectRatio } export { AspectRatio };

View File

@@ -1,50 +1,50 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar" import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Avatar = React.forwardRef< const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>, React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AvatarPrimitive.Root <AvatarPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className className,
)} )}
{...props} {...props}
/> />
)) ));
Avatar.displayName = AvatarPrimitive.Root.displayName Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef< const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>, React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AvatarPrimitive.Image <AvatarPrimitive.Image
ref={ref} ref={ref}
className={cn("aspect-square h-full w-full", className)} className={cn("aspect-square h-full w-full", className)}
{...props} {...props}
/> />
)) ));
AvatarImage.displayName = AvatarPrimitive.Image.displayName AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef< const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>, React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted", "flex h-full w-full items-center justify-center rounded-full bg-muted",
className className,
)} )}
{...props} {...props}
/> />
)) ));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
export { Avatar, AvatarImage, AvatarFallback } export { Avatar, AvatarImage, AvatarFallback };

View File

@@ -1,36 +1,36 @@
import * as React from "react" import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const badgeVariants = cva( const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{ {
variants: { variants: {
variant: { variant: {
default: default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary: secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground", outline: "text-foreground",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
export interface BadgeProps export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>, extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {} VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) { function Badge({ className, variant, ...props }: BadgeProps) {
return ( return (
<div className={cn(badgeVariants({ variant }), className)} {...props} /> <div className={cn(badgeVariants({ variant }), className)} {...props} />
) );
} }
export { Badge, badgeVariants } export { Badge, badgeVariants };

View File

@@ -1,115 +1,115 @@
import * as React from "react" import * as React from "react";
import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons" import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Breadcrumb = React.forwardRef< const Breadcrumb = React.forwardRef<
HTMLElement, HTMLElement,
React.ComponentPropsWithoutRef<"nav"> & { React.ComponentPropsWithoutRef<"nav"> & {
separator?: React.ReactNode separator?: React.ReactNode;
} }
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
Breadcrumb.displayName = "Breadcrumb" Breadcrumb.displayName = "Breadcrumb";
const BreadcrumbList = React.forwardRef< const BreadcrumbList = React.forwardRef<
HTMLOListElement, HTMLOListElement,
React.ComponentPropsWithoutRef<"ol"> React.ComponentPropsWithoutRef<"ol">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ol <ol
ref={ref} ref={ref}
className={cn( className={cn(
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
className className,
)} )}
{...props} {...props}
/> />
)) ));
BreadcrumbList.displayName = "BreadcrumbList" BreadcrumbList.displayName = "BreadcrumbList";
const BreadcrumbItem = React.forwardRef< const BreadcrumbItem = React.forwardRef<
HTMLLIElement, HTMLLIElement,
React.ComponentPropsWithoutRef<"li"> React.ComponentPropsWithoutRef<"li">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<li <li
ref={ref} ref={ref}
className={cn("inline-flex items-center gap-1.5", className)} className={cn("inline-flex items-center gap-1.5", className)}
{...props} {...props}
/> />
)) ));
BreadcrumbItem.displayName = "BreadcrumbItem" BreadcrumbItem.displayName = "BreadcrumbItem";
const BreadcrumbLink = React.forwardRef< const BreadcrumbLink = React.forwardRef<
HTMLAnchorElement, HTMLAnchorElement,
React.ComponentPropsWithoutRef<"a"> & { React.ComponentPropsWithoutRef<"a"> & {
asChild?: boolean asChild?: boolean;
} }
>(({ asChild, className, ...props }, ref) => { >(({ asChild, className, ...props }, ref) => {
const Comp = asChild ? Slot : "a" const Comp = asChild ? Slot : "a";
return ( return (
<Comp <Comp
ref={ref} ref={ref}
className={cn("transition-colors hover:text-foreground", className)} className={cn("transition-colors hover:text-foreground", className)}
{...props} {...props}
/> />
) );
}) });
BreadcrumbLink.displayName = "BreadcrumbLink" BreadcrumbLink.displayName = "BreadcrumbLink";
const BreadcrumbPage = React.forwardRef< const BreadcrumbPage = React.forwardRef<
HTMLSpanElement, HTMLSpanElement,
React.ComponentPropsWithoutRef<"span"> React.ComponentPropsWithoutRef<"span">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<span <span
ref={ref} ref={ref}
role="link" role="link"
aria-disabled="true" aria-disabled="true"
aria-current="page" aria-current="page"
className={cn("font-normal text-foreground", className)} className={cn("font-normal text-foreground", className)}
{...props} {...props}
/> />
)) ));
BreadcrumbPage.displayName = "BreadcrumbPage" BreadcrumbPage.displayName = "BreadcrumbPage";
const BreadcrumbSeparator = ({ const BreadcrumbSeparator = ({
children, children,
className, className,
...props ...props
}: React.ComponentProps<"li">) => ( }: React.ComponentProps<"li">) => (
<li <li
role="presentation" role="presentation"
aria-hidden="true" aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)} className={cn("[&>svg]:size-3.5", className)}
{...props} {...props}
> >
{children ?? <ChevronRightIcon />} {children ?? <ChevronRightIcon />}
</li> </li>
) );
BreadcrumbSeparator.displayName = "BreadcrumbSeparator" BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
const BreadcrumbEllipsis = ({ const BreadcrumbEllipsis = ({
className, className,
...props ...props
}: React.ComponentProps<"span">) => ( }: React.ComponentProps<"span">) => (
<span <span
role="presentation" role="presentation"
aria-hidden="true" aria-hidden="true"
className={cn("flex h-9 w-9 items-center justify-center", className)} className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props} {...props}
> >
<DotsHorizontalIcon className="h-4 w-4" /> <DotsHorizontalIcon className="h-4 w-4" />
<span className="sr-only">More</span> <span className="sr-only">More</span>
</span> </span>
) );
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
export { export {
Breadcrumb, Breadcrumb,
BreadcrumbList, BreadcrumbList,
BreadcrumbItem, BreadcrumbItem,
BreadcrumbLink, BreadcrumbLink,
BreadcrumbPage, BreadcrumbPage,
BreadcrumbSeparator, BreadcrumbSeparator,
BreadcrumbEllipsis, BreadcrumbEllipsis,
} };

View File

@@ -1,57 +1,57 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{ {
variants: { variants: {
variant: { variant: {
default: default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90", "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground", ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
default: "h-9 px-4 py-2", default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs", sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8", lg: "h-10 rounded-md px-8",
icon: "h-9 w-9", icon: "h-9 w-9",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
) );
export interface ButtonProps export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>, extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> { VariantProps<typeof buttonVariants> {
asChild?: boolean asChild?: boolean;
} }
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => { ({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button" const Comp = asChild ? Slot : "button";
return ( return (
<Comp <Comp
className={cn(buttonVariants({ variant, size, className }))} className={cn(buttonVariants({ variant, size, className }))}
ref={ref} ref={ref}
{...props} {...props}
/> />
) );
} },
) );
Button.displayName = "Button" Button.displayName = "Button";
export { Button, buttonVariants } export { Button, buttonVariants };

View File

@@ -1,72 +1,72 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons" import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
import { DayPicker } from "react-day-picker" import { DayPicker } from "react-day-picker";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button" import { buttonVariants } from "@/components/ui/button";
export type CalendarProps = React.ComponentProps<typeof DayPicker> export type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar({ function Calendar({
className, className,
classNames, classNames,
showOutsideDays = true, showOutsideDays = true,
...props ...props
}: CalendarProps) { }: CalendarProps) {
return ( return (
<DayPicker <DayPicker
showOutsideDays={showOutsideDays} showOutsideDays={showOutsideDays}
className={cn("p-3", className)} className={cn("p-3", className)}
classNames={{ classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0", months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4", month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center", caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium", caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center", nav: "space-x-1 flex items-center",
nav_button: cn( nav_button: cn(
buttonVariants({ variant: "outline" }), buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
), ),
nav_button_previous: "absolute left-1", nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1", nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1", table: "w-full border-collapse space-y-1",
head_row: "flex", head_row: "flex",
head_cell: head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]", "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2", row: "flex w-full mt-2",
cell: cn( cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md", "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range" props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md" : "[&:has([aria-selected])]:rounded-md",
), ),
day: cn( day: cn(
buttonVariants({ variant: "ghost" }), buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100" "h-8 w-8 p-0 font-normal aria-selected:opacity-100",
), ),
day_range_start: "day-range-start", day_range_start: "day-range-start",
day_range_end: "day-range-end", day_range_end: "day-range-end",
day_selected: day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground", day_today: "bg-accent text-accent-foreground",
day_outside: day_outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
day_disabled: "text-muted-foreground opacity-50", day_disabled: "text-muted-foreground opacity-50",
day_range_middle: day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground", "aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible", day_hidden: "invisible",
...classNames, ...classNames,
}} }}
components={{ components={{
IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />, IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />, IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
}} }}
{...props} {...props}
/> />
) );
} }
Calendar.displayName = "Calendar" Calendar.displayName = "Calendar";
export { Calendar } export { Calendar };

View File

@@ -5,122 +5,122 @@ import React, { useMemo, useRef } from "react";
import * as THREE from "three"; import * as THREE from "three";
export const CanvasRevealEffect = ({ export const CanvasRevealEffect = ({
animationSpeed = 0.4, animationSpeed = 0.4,
opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1], opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],
colors = [[0, 255, 255]], colors = [[0, 255, 255]],
containerClassName, containerClassName,
dotSize, dotSize,
showGradient = true, showGradient = true,
}: { }: {
/** /**
* 0.1 - slower * 0.1 - slower
* 1.0 - faster * 1.0 - faster
*/ */
animationSpeed?: number; animationSpeed?: number;
opacities?: number[]; opacities?: number[];
colors?: number[][]; colors?: number[][];
containerClassName?: string; containerClassName?: string;
dotSize?: number; dotSize?: number;
showGradient?: boolean; showGradient?: boolean;
}) => { }) => {
return ( return (
<div className={cn("h-full relative bg-white w-full", containerClassName)}> <div className={cn("h-full relative bg-white w-full", containerClassName)}>
<div className="h-full w-full"> <div className="h-full w-full">
<DotMatrix <DotMatrix
colors={colors ?? [[0, 255, 255]]} colors={colors ?? [[0, 255, 255]]}
dotSize={dotSize ?? 3} dotSize={dotSize ?? 3}
opacities={ opacities={
opacities ?? [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1] opacities ?? [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1]
} }
shader={` shader={`
float animation_speed_factor = ${animationSpeed.toFixed(1)}; float animation_speed_factor = ${animationSpeed.toFixed(1)};
float intro_offset = distance(u_resolution / 2.0 / u_total_size, st2) * 0.01 + (random(st2) * 0.15); float intro_offset = distance(u_resolution / 2.0 / u_total_size, st2) * 0.01 + (random(st2) * 0.15);
opacity *= step(intro_offset, u_time * animation_speed_factor); opacity *= step(intro_offset, u_time * animation_speed_factor);
opacity *= clamp((1.0 - step(intro_offset + 0.1, u_time * animation_speed_factor)) * 1.25, 1.0, 1.25); opacity *= clamp((1.0 - step(intro_offset + 0.1, u_time * animation_speed_factor)) * 1.25, 1.0, 1.25);
`} `}
center={["x", "y"]} center={["x", "y"]}
/> />
</div> </div>
{showGradient && ( {showGradient && (
<div className="absolute inset-0 bg-gradient-to-t from-gray-950 to-[84%]" /> <div className="absolute inset-0 bg-gradient-to-t from-gray-950 to-[84%]" />
)} )}
</div> </div>
); );
}; };
interface DotMatrixProps { interface DotMatrixProps {
colors?: number[][]; colors?: number[][];
opacities?: number[]; opacities?: number[];
totalSize?: number; totalSize?: number;
dotSize?: number; dotSize?: number;
shader?: string; shader?: string;
center?: ("x" | "y")[]; center?: ("x" | "y")[];
} }
const DotMatrix: React.FC<DotMatrixProps> = ({ const DotMatrix: React.FC<DotMatrixProps> = ({
colors = [[0, 0, 0]], colors = [[0, 0, 0]],
opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14], opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],
totalSize = 4, totalSize = 4,
dotSize = 2, dotSize = 2,
shader = "", shader = "",
center = ["x", "y"], center = ["x", "y"],
}) => { }) => {
const uniforms = React.useMemo(() => { const uniforms = React.useMemo(() => {
let colorsArray = [ let colorsArray = [
colors[0], colors[0],
colors[0], colors[0],
colors[0], colors[0],
colors[0], colors[0],
colors[0], colors[0],
colors[0], colors[0],
]; ];
if (colors.length === 2) { if (colors.length === 2) {
colorsArray = [ colorsArray = [
colors[0], colors[0],
colors[0], colors[0],
colors[0], colors[0],
colors[1], colors[1],
colors[1], colors[1],
colors[1], colors[1],
]; ];
} else if (colors.length === 3) { } else if (colors.length === 3) {
colorsArray = [ colorsArray = [
colors[0], colors[0],
colors[0], colors[0],
colors[1], colors[1],
colors[1], colors[1],
colors[2], colors[2],
colors[2], colors[2],
]; ];
} }
return { return {
u_colors: { u_colors: {
value: colorsArray.map((color) => [ value: colorsArray.map((color) => [
color[0] / 255, color[0] / 255,
color[1] / 255, color[1] / 255,
color[2] / 255, color[2] / 255,
]), ]),
type: "uniform3fv", type: "uniform3fv",
}, },
u_opacities: { u_opacities: {
value: opacities, value: opacities,
type: "uniform1fv", type: "uniform1fv",
}, },
u_total_size: { u_total_size: {
value: totalSize, value: totalSize,
type: "uniform1f", type: "uniform1f",
}, },
u_dot_size: { u_dot_size: {
value: dotSize, value: dotSize,
type: "uniform1f", type: "uniform1f",
}, },
}; };
}, [colors, opacities, totalSize, dotSize]); }, [colors, opacities, totalSize, dotSize]);
return ( return (
<Shader <Shader
source={` source={`
precision mediump float; precision mediump float;
in vec2 fragCoord; in vec2 fragCoord;
@@ -140,14 +140,16 @@ const DotMatrix: React.FC<DotMatrixProps> = ({
} }
void main() { void main() {
vec2 st = fragCoord.xy; vec2 st = fragCoord.xy;
${center.includes("x") ${
? "st.x -= abs(floor((mod(u_resolution.x, u_total_size) - u_dot_size) * 0.5));" center.includes("x")
: "" ? "st.x -= abs(floor((mod(u_resolution.x, u_total_size) - u_dot_size) * 0.5));"
} : ""
${center.includes("y") }
? "st.y -= abs(floor((mod(u_resolution.y, u_total_size) - u_dot_size) * 0.5));" ${
: "" center.includes("y")
} ? "st.y -= abs(floor((mod(u_resolution.y, u_total_size) - u_dot_size) * 0.5));"
: ""
}
float opacity = step(0.0, st.x); float opacity = step(0.0, st.x);
opacity *= step(0.0, st.y); opacity *= step(0.0, st.y);
@@ -167,95 +169,95 @@ const DotMatrix: React.FC<DotMatrixProps> = ({
fragColor = vec4(color, opacity); fragColor = vec4(color, opacity);
fragColor.rgb *= fragColor.a; fragColor.rgb *= fragColor.a;
}`} }`}
uniforms={uniforms} uniforms={uniforms}
maxFps={60} maxFps={60}
/> />
); );
}; };
type Uniforms = { type Uniforms = {
[key: string]: { [key: string]: {
value: number[] | number[][] | number; value: number[] | number[][] | number;
type: string; type: string;
}; };
}; };
const ShaderMaterial = ({ const ShaderMaterial = ({
source, source,
uniforms, uniforms,
maxFps = 60, maxFps = 60,
}: { }: {
source: string; source: string;
hovered?: boolean; hovered?: boolean;
maxFps?: number; maxFps?: number;
uniforms: Uniforms; uniforms: Uniforms;
}) => { }) => {
const { size } = useThree(); const { size } = useThree();
const ref = useRef<THREE.Mesh>(); const ref = useRef<THREE.Mesh>();
let lastFrameTime = 0; let lastFrameTime = 0;
useFrame(({ clock }) => { useFrame(({ clock }) => {
if (!ref.current) return; if (!ref.current) return;
const timestamp = clock.getElapsedTime(); const timestamp = clock.getElapsedTime();
if (timestamp - lastFrameTime < 1 / maxFps) { if (timestamp - lastFrameTime < 1 / maxFps) {
return; return;
} }
lastFrameTime = timestamp; lastFrameTime = timestamp;
const material: any = ref.current.material; const material: any = ref.current.material;
const timeLocation = material.uniforms.u_time; const timeLocation = material.uniforms.u_time;
timeLocation.value = timestamp; timeLocation.value = timestamp;
}); });
const getUniforms = () => { const getUniforms = () => {
const preparedUniforms: any = {}; const preparedUniforms: any = {};
for (const uniformName in uniforms) { for (const uniformName in uniforms) {
const uniform: any = uniforms[uniformName]; const uniform: any = uniforms[uniformName];
switch (uniform.type) { switch (uniform.type) {
case "uniform1f": case "uniform1f":
preparedUniforms[uniformName] = { value: uniform.value, type: "1f" }; preparedUniforms[uniformName] = { value: uniform.value, type: "1f" };
break; break;
case "uniform3f": case "uniform3f":
preparedUniforms[uniformName] = { preparedUniforms[uniformName] = {
value: new THREE.Vector3().fromArray(uniform.value), value: new THREE.Vector3().fromArray(uniform.value),
type: "3f", type: "3f",
}; };
break; break;
case "uniform1fv": case "uniform1fv":
preparedUniforms[uniformName] = { value: uniform.value, type: "1fv" }; preparedUniforms[uniformName] = { value: uniform.value, type: "1fv" };
break; break;
case "uniform3fv": case "uniform3fv":
preparedUniforms[uniformName] = { preparedUniforms[uniformName] = {
value: uniform.value.map((v: number[]) => value: uniform.value.map((v: number[]) =>
new THREE.Vector3().fromArray(v) new THREE.Vector3().fromArray(v),
), ),
type: "3fv", type: "3fv",
}; };
break; break;
case "uniform2f": case "uniform2f":
preparedUniforms[uniformName] = { preparedUniforms[uniformName] = {
value: new THREE.Vector2().fromArray(uniform.value), value: new THREE.Vector2().fromArray(uniform.value),
type: "2f", type: "2f",
}; };
break; break;
default: default:
console.error(`Invalid uniform type for '${uniformName}'.`); console.error(`Invalid uniform type for '${uniformName}'.`);
break; break;
} }
} }
preparedUniforms["u_time"] = { value: 0, type: "1f" }; preparedUniforms["u_time"] = { value: 0, type: "1f" };
preparedUniforms["u_resolution"] = { preparedUniforms["u_resolution"] = {
value: new THREE.Vector2(size.width * 2, size.height * 2), value: new THREE.Vector2(size.width * 2, size.height * 2),
}; // Initialize u_resolution }; // Initialize u_resolution
return preparedUniforms; return preparedUniforms;
}; };
// Shader material // Shader material
const material = useMemo(() => { const material = useMemo(() => {
const materialObject = new THREE.ShaderMaterial({ const materialObject = new THREE.ShaderMaterial({
vertexShader: ` vertexShader: `
precision mediump float; precision mediump float;
in vec2 coordinates; in vec2 coordinates;
uniform vec2 u_resolution; uniform vec2 u_resolution;
@@ -268,39 +270,39 @@ const ShaderMaterial = ({
fragCoord.y = u_resolution.y - fragCoord.y; fragCoord.y = u_resolution.y - fragCoord.y;
} }
`, `,
fragmentShader: source, fragmentShader: source,
uniforms: getUniforms(), uniforms: getUniforms(),
glslVersion: THREE.GLSL3, glslVersion: THREE.GLSL3,
blending: THREE.CustomBlending, blending: THREE.CustomBlending,
blendSrc: THREE.SrcAlphaFactor, blendSrc: THREE.SrcAlphaFactor,
blendDst: THREE.OneFactor, blendDst: THREE.OneFactor,
}); });
return materialObject; return materialObject;
}, [size.width, size.height, source]); }, [size.width, size.height, source]);
return ( return (
<mesh ref={ref as any}> <mesh ref={ref as any}>
<planeGeometry args={[2, 2]} /> <planeGeometry args={[2, 2]} />
<primitive object={material} attach="material" /> <primitive object={material} attach="material" />
</mesh> </mesh>
); );
}; };
const Shader: React.FC<ShaderProps> = ({ source, uniforms, maxFps = 60 }) => { const Shader: React.FC<ShaderProps> = ({ source, uniforms, maxFps = 60 }) => {
return ( return (
<Canvas className="absolute inset-0 h-full w-full"> <Canvas className="absolute inset-0 h-full w-full">
<ShaderMaterial source={source} uniforms={uniforms} maxFps={maxFps} /> <ShaderMaterial source={source} uniforms={uniforms} maxFps={maxFps} />
</Canvas> </Canvas>
); );
}; };
interface ShaderProps { interface ShaderProps {
source: string; source: string;
uniforms: { uniforms: {
[key: string]: { [key: string]: {
value: number[] | number[][] | number; value: number[] | number[][] | number;
type: string; type: string;
}; };
}; };
maxFps?: number; maxFps?: number;
} }

View File

@@ -1,76 +1,83 @@
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Card = React.forwardRef< const Card = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"rounded-xl border bg-card text-card-foreground shadow", "rounded-xl border bg-card text-card-foreground shadow",
className className,
)} )}
{...props} {...props}
/> />
)) ));
Card.displayName = "Card" Card.displayName = "Card";
const CardHeader = React.forwardRef< const CardHeader = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)} className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props} {...props}
/> />
)) ));
CardHeader.displayName = "CardHeader" CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef< const CardTitle = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement> React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<h3 <h3
ref={ref} ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)} className={cn("font-semibold leading-none tracking-tight", className)}
{...props} {...props}
/> />
)) ));
CardTitle.displayName = "CardTitle" CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef< const CardDescription = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement> React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<p <p
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
CardDescription.displayName = "CardDescription" CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef< const CardContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
)) ));
CardContent.displayName = "CardContent" CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef< const CardFooter = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("flex items-center p-6 pt-0", className)} className={cn("flex items-center p-6 pt-0", className)}
{...props} {...props}
/> />
)) ));
CardFooter.displayName = "CardFooter" CardFooter.displayName = "CardFooter";
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};

View File

@@ -1,262 +1,262 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons" import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
import useEmblaCarousel, { import useEmblaCarousel, {
type UseEmblaCarouselType, type UseEmblaCarouselType,
} from "embla-carousel-react" } from "embla-carousel-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
type CarouselApi = UseEmblaCarouselType[1] type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel> type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0] type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1] type CarouselPlugin = UseCarouselParameters[1];
type CarouselProps = { type CarouselProps = {
opts?: CarouselOptions opts?: CarouselOptions;
plugins?: CarouselPlugin plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical" orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void setApi?: (api: CarouselApi) => void;
} };
type CarouselContextProps = { type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0] carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1] api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void scrollPrev: () => void;
scrollNext: () => void scrollNext: () => void;
canScrollPrev: boolean canScrollPrev: boolean;
canScrollNext: boolean canScrollNext: boolean;
} & CarouselProps } & CarouselProps;
const CarouselContext = React.createContext<CarouselContextProps | null>(null) const CarouselContext = React.createContext<CarouselContextProps | null>(null);
function useCarousel() { function useCarousel() {
const context = React.useContext(CarouselContext) const context = React.useContext(CarouselContext);
if (!context) { if (!context) {
throw new Error("useCarousel must be used within a <Carousel />") throw new Error("useCarousel must be used within a <Carousel />");
} }
return context return context;
} }
const Carousel = React.forwardRef< const Carousel = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & CarouselProps React.HTMLAttributes<HTMLDivElement> & CarouselProps
>( >(
( (
{ {
orientation = "horizontal", orientation = "horizontal",
opts, opts,
setApi, setApi,
plugins, plugins,
className, className,
children, children,
...props ...props
}, },
ref ref,
) => { ) => {
const [carouselRef, api] = useEmblaCarousel( const [carouselRef, api] = useEmblaCarousel(
{ {
...opts, ...opts,
axis: orientation === "horizontal" ? "x" : "y", axis: orientation === "horizontal" ? "x" : "y",
}, },
plugins plugins,
) );
const [canScrollPrev, setCanScrollPrev] = React.useState(false) const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false) const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback((api: CarouselApi) => { const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) { if (!api) {
return return;
} }
setCanScrollPrev(api.canScrollPrev()) setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext()) setCanScrollNext(api.canScrollNext());
}, []) }, []);
const scrollPrev = React.useCallback(() => { const scrollPrev = React.useCallback(() => {
api?.scrollPrev() api?.scrollPrev();
}, [api]) }, [api]);
const scrollNext = React.useCallback(() => { const scrollNext = React.useCallback(() => {
api?.scrollNext() api?.scrollNext();
}, [api]) }, [api]);
const handleKeyDown = React.useCallback( const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") { if (event.key === "ArrowLeft") {
event.preventDefault() event.preventDefault();
scrollPrev() scrollPrev();
} else if (event.key === "ArrowRight") { } else if (event.key === "ArrowRight") {
event.preventDefault() event.preventDefault();
scrollNext() scrollNext();
} }
}, },
[scrollPrev, scrollNext] [scrollPrev, scrollNext],
) );
React.useEffect(() => { React.useEffect(() => {
if (!api || !setApi) { if (!api || !setApi) {
return return;
} }
setApi(api) setApi(api);
}, [api, setApi]) }, [api, setApi]);
React.useEffect(() => { React.useEffect(() => {
if (!api) { if (!api) {
return return;
} }
onSelect(api) onSelect(api);
api.on("reInit", onSelect) api.on("reInit", onSelect);
api.on("select", onSelect) api.on("select", onSelect);
return () => { return () => {
api?.off("select", onSelect) api?.off("select", onSelect);
} };
}, [api, onSelect]) }, [api, onSelect]);
return ( return (
<CarouselContext.Provider <CarouselContext.Provider
value={{ value={{
carouselRef, carouselRef,
api: api, api: api,
opts, opts,
orientation: orientation:
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"), orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev, scrollPrev,
scrollNext, scrollNext,
canScrollPrev, canScrollPrev,
canScrollNext, canScrollNext,
}} }}
> >
<div <div
ref={ref} ref={ref}
onKeyDownCapture={handleKeyDown} onKeyDownCapture={handleKeyDown}
className={cn("relative", className)} className={cn("relative", className)}
role="region" role="region"
aria-roledescription="carousel" aria-roledescription="carousel"
{...props} {...props}
> >
{children} {children}
</div> </div>
</CarouselContext.Provider> </CarouselContext.Provider>
) );
} },
) );
Carousel.displayName = "Carousel" Carousel.displayName = "Carousel";
const CarouselContent = React.forwardRef< const CarouselContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel() const { carouselRef, orientation } = useCarousel();
return ( return (
<div ref={carouselRef} className="overflow-hidden"> <div ref={carouselRef} className="overflow-hidden">
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"flex", "flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className className,
)} )}
{...props} {...props}
/> />
</div> </div>
) );
}) });
CarouselContent.displayName = "CarouselContent" CarouselContent.displayName = "CarouselContent";
const CarouselItem = React.forwardRef< const CarouselItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
const { orientation } = useCarousel() const { orientation } = useCarousel();
return ( return (
<div <div
ref={ref} ref={ref}
role="group" role="group"
aria-roledescription="slide" aria-roledescription="slide"
className={cn( className={cn(
"min-w-0 shrink-0 grow-0 basis-full", "min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4", orientation === "horizontal" ? "pl-4" : "pt-4",
className className,
)} )}
{...props} {...props}
/> />
) );
}) });
CarouselItem.displayName = "CarouselItem" CarouselItem.displayName = "CarouselItem";
const CarouselPrevious = React.forwardRef< const CarouselPrevious = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
React.ComponentProps<typeof Button> React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => { >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel() const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return ( return (
<Button <Button
ref={ref} ref={ref}
variant={variant} variant={variant}
size={size} size={size}
className={cn( className={cn(
"absolute h-8 w-8 rounded-full", "absolute h-8 w-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2" ? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90", : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollPrev} disabled={!canScrollPrev}
onClick={scrollPrev} onClick={scrollPrev}
{...props} {...props}
> >
<ArrowLeftIcon className="h-4 w-4" /> <ArrowLeftIcon className="h-4 w-4" />
<span className="sr-only">Previous slide</span> <span className="sr-only">Previous slide</span>
</Button> </Button>
) );
}) });
CarouselPrevious.displayName = "CarouselPrevious" CarouselPrevious.displayName = "CarouselPrevious";
const CarouselNext = React.forwardRef< const CarouselNext = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
React.ComponentProps<typeof Button> React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => { >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel() const { orientation, scrollNext, canScrollNext } = useCarousel();
return ( return (
<Button <Button
ref={ref} ref={ref}
variant={variant} variant={variant}
size={size} size={size}
className={cn( className={cn(
"absolute h-8 w-8 rounded-full", "absolute h-8 w-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2" ? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollNext} disabled={!canScrollNext}
onClick={scrollNext} onClick={scrollNext}
{...props} {...props}
> >
<ArrowRightIcon className="h-4 w-4" /> <ArrowRightIcon className="h-4 w-4" />
<span className="sr-only">Next slide</span> <span className="sr-only">Next slide</span>
</Button> </Button>
) );
}) });
CarouselNext.displayName = "CarouselNext" CarouselNext.displayName = "CarouselNext";
export { export {
type CarouselApi, type CarouselApi,
Carousel, Carousel,
CarouselContent, CarouselContent,
CarouselItem, CarouselItem,
CarouselPrevious, CarouselPrevious,
CarouselNext, CarouselNext,
} };

View File

@@ -1,370 +1,370 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as RechartsPrimitive from "recharts" import * as RechartsPrimitive from "recharts";
import { import {
NameType, NameType,
Payload, Payload,
ValueType, ValueType,
} from "recharts/types/component/DefaultTooltipContent" } from "recharts/types/component/DefaultTooltipContent";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
// Format: { THEME_NAME: CSS_SELECTOR } // Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const const THEMES = { light: "", dark: ".dark" } as const;
export type ChartConfig = { export type ChartConfig = {
[k in string]: { [k in string]: {
label?: React.ReactNode label?: React.ReactNode;
icon?: React.ComponentType icon?: React.ComponentType;
} & ( } & (
| { color?: string; theme?: never } | { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> } | { color?: never; theme: Record<keyof typeof THEMES, string> }
) );
} };
type ChartContextProps = { type ChartContextProps = {
config: ChartConfig config: ChartConfig;
} };
const ChartContext = React.createContext<ChartContextProps | null>(null) const ChartContext = React.createContext<ChartContextProps | null>(null);
function useChart() { function useChart() {
const context = React.useContext(ChartContext) const context = React.useContext(ChartContext);
if (!context) { if (!context) {
throw new Error("useChart must be used within a <ChartContainer />") throw new Error("useChart must be used within a <ChartContainer />");
} }
return context return context;
} }
const ChartContainer = React.forwardRef< const ChartContainer = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.ComponentProps<"div"> & { React.ComponentProps<"div"> & {
config: ChartConfig config: ChartConfig;
children: React.ComponentProps< children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer typeof RechartsPrimitive.ResponsiveContainer
>["children"] >["children"];
} }
>(({ id, className, children, config, ...props }, ref) => { >(({ id, className, children, config, ...props }, ref) => {
const uniqueId = React.useId() const uniqueId = React.useId();
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
return ( return (
<ChartContext.Provider value={{ config }}> <ChartContext.Provider value={{ config }}>
<div <div
data-chart={chartId} data-chart={chartId}
ref={ref} ref={ref}
className={cn( className={cn(
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none", "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
className className,
)} )}
{...props} {...props}
> >
<ChartStyle id={chartId} config={config} /> <ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer> <RechartsPrimitive.ResponsiveContainer>
{children} {children}
</RechartsPrimitive.ResponsiveContainer> </RechartsPrimitive.ResponsiveContainer>
</div> </div>
</ChartContext.Provider> </ChartContext.Provider>
) );
}) });
ChartContainer.displayName = "Chart" ChartContainer.displayName = "Chart";
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter( const colorConfig = Object.entries(config).filter(
([_, config]) => config.theme || config.color ([_, config]) => config.theme || config.color,
) );
if (!colorConfig.length) { if (!colorConfig.length) {
return null return null;
} }
return ( return (
<style <style
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: Object.entries(THEMES) __html: Object.entries(THEMES)
.map( .map(
([theme, prefix]) => ` ([theme, prefix]) => `
${prefix} [data-chart=${id}] { ${prefix} [data-chart=${id}] {
${colorConfig ${colorConfig
.map(([key, itemConfig]) => { .map(([key, itemConfig]) => {
const color = const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color itemConfig.color;
return color ? ` --color-${key}: ${color};` : null return color ? ` --color-${key}: ${color};` : null;
}) })
.join("\n")} .join("\n")}
}
`
)
.join("\n"),
}}
/>
)
} }
`,
)
.join("\n"),
}}
/>
);
};
const ChartTooltip = RechartsPrimitive.Tooltip const ChartTooltip = RechartsPrimitive.Tooltip;
const ChartTooltipContent = React.forwardRef< const ChartTooltipContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> & React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & { React.ComponentProps<"div"> & {
hideLabel?: boolean hideLabel?: boolean;
hideIndicator?: boolean hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed" indicator?: "line" | "dot" | "dashed";
nameKey?: string nameKey?: string;
labelKey?: string labelKey?: string;
} }
>( >(
( (
{ {
active, active,
payload, payload,
className, className,
indicator = "dot", indicator = "dot",
hideLabel = false, hideLabel = false,
hideIndicator = false, hideIndicator = false,
label, label,
labelFormatter, labelFormatter,
labelClassName, labelClassName,
formatter, formatter,
color, color,
nameKey, nameKey,
labelKey, labelKey,
}, },
ref ref,
) => { ) => {
const { config } = useChart() const { config } = useChart();
const tooltipLabel = React.useMemo(() => { const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) { if (hideLabel || !payload?.length) {
return null return null;
} }
const [item] = payload const [item] = payload;
const key = `${labelKey || item.dataKey || item.name || "value"}` const key = `${labelKey || item.dataKey || item.name || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
const value = const value =
!labelKey && typeof label === "string" !labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label ? config[label as keyof typeof config]?.label || label
: itemConfig?.label : itemConfig?.label;
if (labelFormatter) { if (labelFormatter) {
return ( return (
<div className={cn("font-medium", labelClassName)}> <div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)} {labelFormatter(value, payload)}
</div> </div>
) );
} }
if (!value) { if (!value) {
return null return null;
} }
return <div className={cn("font-medium", labelClassName)}>{value}</div> return <div className={cn("font-medium", labelClassName)}>{value}</div>;
}, [ }, [
label, label,
labelFormatter, labelFormatter,
payload, payload,
hideLabel, hideLabel,
labelClassName, labelClassName,
config, config,
labelKey, labelKey,
]) ]);
if (!active || !payload?.length) { if (!active || !payload?.length) {
return null return null;
} }
const nestLabel = payload.length === 1 && indicator !== "dot" const nestLabel = payload.length === 1 && indicator !== "dot";
return ( return (
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
className className,
)} )}
> >
{!nestLabel ? tooltipLabel : null} {!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{payload.map((item, index) => { {payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}` const key = `${nameKey || item.name || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
const indicatorColor = color || item.payload.fill || item.color const indicatorColor = color || item.payload.fill || item.color;
return ( return (
<div <div
key={item.dataKey} key={item.dataKey}
className={cn( className={cn(
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center" indicator === "dot" && "items-center",
)} )}
> >
{formatter && item?.value !== undefined && item.name ? ( {formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload) formatter(item.value, item.name, item, index, item.payload)
) : ( ) : (
<> <>
{itemConfig?.icon ? ( {itemConfig?.icon ? (
<itemConfig.icon /> <itemConfig.icon />
) : ( ) : (
!hideIndicator && ( !hideIndicator && (
<div <div
className={cn( className={cn(
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
{ {
"h-2.5 w-2.5": indicator === "dot", "h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line", "w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent": "w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed", indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed", "my-0.5": nestLabel && indicator === "dashed",
} },
)} )}
style={ style={
{ {
"--color-bg": indicatorColor, "--color-bg": indicatorColor,
"--color-border": indicatorColor, "--color-border": indicatorColor,
} as React.CSSProperties } as React.CSSProperties
} }
/> />
) )
)} )}
<div <div
className={cn( className={cn(
"flex flex-1 justify-between leading-none", "flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center" nestLabel ? "items-end" : "items-center",
)} )}
> >
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null} {nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground"> <span className="text-muted-foreground">
{itemConfig?.label || item.name} {itemConfig?.label || item.name}
</span> </span>
</div> </div>
{item.value && ( {item.value && (
<span className="font-mono font-medium tabular-nums text-foreground"> <span className="font-mono font-medium tabular-nums text-foreground">
{item.value.toLocaleString()} {item.value.toLocaleString()}
</span> </span>
)} )}
</div> </div>
</> </>
)} )}
</div> </div>
) );
})} })}
</div> </div>
</div> </div>
) );
} },
) );
ChartTooltipContent.displayName = "ChartTooltip" ChartTooltipContent.displayName = "ChartTooltip";
const ChartLegend = RechartsPrimitive.Legend const ChartLegend = RechartsPrimitive.Legend;
const ChartLegendContent = React.forwardRef< const ChartLegendContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.ComponentProps<"div"> & React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean hideIcon?: boolean;
nameKey?: string nameKey?: string;
} }
>( >(
( (
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
ref ref,
) => { ) => {
const { config } = useChart() const { config } = useChart();
if (!payload?.length) { if (!payload?.length) {
return null return null;
} }
return ( return (
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"flex items-center justify-center gap-4", "flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3", verticalAlign === "top" ? "pb-3" : "pt-3",
className className,
)} )}
> >
{payload.map((item) => { {payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}` const key = `${nameKey || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key);
return ( return (
<div <div
key={item.value} key={item.value}
className={cn( className={cn(
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
)} )}
> >
{itemConfig?.icon && !hideIcon ? ( {itemConfig?.icon && !hideIcon ? (
<itemConfig.icon /> <itemConfig.icon />
) : ( ) : (
<div <div
className="h-2 w-2 shrink-0 rounded-[2px]" className="h-2 w-2 shrink-0 rounded-[2px]"
style={{ style={{
backgroundColor: item.color, backgroundColor: item.color,
}} }}
/> />
)} )}
{itemConfig?.label} {itemConfig?.label}
</div> </div>
) );
})} })}
</div> </div>
) );
} },
) );
ChartLegendContent.displayName = "ChartLegend" ChartLegendContent.displayName = "ChartLegend";
// Helper to extract item config from a payload. // Helper to extract item config from a payload.
function getPayloadConfigFromPayload( function getPayloadConfigFromPayload(
config: ChartConfig, config: ChartConfig,
payload: unknown, payload: unknown,
key: string key: string,
) { ) {
if (typeof payload !== "object" || payload === null) { if (typeof payload !== "object" || payload === null) {
return undefined return undefined;
} }
const payloadPayload = const payloadPayload =
"payload" in payload && "payload" in payload &&
typeof payload.payload === "object" && typeof payload.payload === "object" &&
payload.payload !== null payload.payload !== null
? payload.payload ? payload.payload
: undefined : undefined;
let configLabelKey: string = key let configLabelKey: string = key;
if ( if (
key in payload && key in payload &&
typeof payload[key as keyof typeof payload] === "string" typeof payload[key as keyof typeof payload] === "string"
) { ) {
configLabelKey = payload[key as keyof typeof payload] as string configLabelKey = payload[key as keyof typeof payload] as string;
} else if ( } else if (
payloadPayload && payloadPayload &&
key in payloadPayload && key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string" typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) { ) {
configLabelKey = payloadPayload[ configLabelKey = payloadPayload[
key as keyof typeof payloadPayload key as keyof typeof payloadPayload
] as string ] as string;
} }
return configLabelKey in config return configLabelKey in config
? config[configLabelKey] ? config[configLabelKey]
: config[key as keyof typeof config] : config[key as keyof typeof config];
} }
export { export {
ChartContainer, ChartContainer,
ChartTooltip, ChartTooltip,
ChartTooltipContent, ChartTooltipContent,
ChartLegend, ChartLegend,
ChartLegendContent, ChartLegendContent,
ChartStyle, ChartStyle,
} };

View File

@@ -1,30 +1,30 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox" import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon } from "@radix-ui/react-icons" import { CheckIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Checkbox = React.forwardRef< const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>, React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", "peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className className,
)} )}
{...props} {...props}
> >
<CheckboxPrimitive.Indicator <CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")} className={cn("flex items-center justify-center text-current")}
> >
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</CheckboxPrimitive.Indicator> </CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root> </CheckboxPrimitive.Root>
)) ));
Checkbox.displayName = CheckboxPrimitive.Root.displayName Checkbox.displayName = CheckboxPrimitive.Root.displayName;
export { Checkbox } export { Checkbox };

View File

@@ -1,11 +1,11 @@
"use client" "use client";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
const Collapsible = CollapsiblePrimitive.Root const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
export { Collapsible, CollapsibleTrigger, CollapsibleContent } export { Collapsible, CollapsibleTrigger, CollapsibleContent };

View File

@@ -1,155 +1,155 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { type DialogProps } from "@radix-ui/react-dialog" import { type DialogProps } from "@radix-ui/react-dialog";
import { MagnifyingGlassIcon } from "@radix-ui/react-icons" import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
import { Command as CommandPrimitive } from "cmdk" import { Command as CommandPrimitive } from "cmdk";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Dialog, DialogContent } from "@/components/ui/dialog" import { Dialog, DialogContent } from "@/components/ui/dialog";
const Command = React.forwardRef< const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>, React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive> React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive <CommandPrimitive
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className className,
)} )}
{...props} {...props}
/> />
)) ));
Command.displayName = CommandPrimitive.displayName Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {} interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => { const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return ( return (
<Dialog {...props}> <Dialog {...props}>
<DialogContent className="overflow-hidden p-0"> <DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children} {children}
</Command> </Command>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
) );
} };
const CommandInput = React.forwardRef< const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>, React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper=""> <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" /> <MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input <CommandPrimitive.Input
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
</div> </div>
)) ));
CommandInput.displayName = CommandPrimitive.Input.displayName CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef< const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>, React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.List <CommandPrimitive.List
ref={ref} ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props} {...props}
/> />
)) ));
CommandList.displayName = CommandPrimitive.List.displayName CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef< const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>, React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => ( >((props, ref) => (
<CommandPrimitive.Empty <CommandPrimitive.Empty
ref={ref} ref={ref}
className="py-6 text-center text-sm" className="py-6 text-center text-sm"
{...props} {...props}
/> />
)) ));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef< const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>, React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.Group <CommandPrimitive.Group
ref={ref} ref={ref}
className={cn( className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
)) ));
CommandGroup.displayName = CommandPrimitive.Group.displayName CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef< const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>, React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.Separator <CommandPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 h-px bg-border", className)} className={cn("-mx-1 h-px bg-border", className)}
{...props} {...props}
/> />
)) ));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef< const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>, React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.Item <CommandPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
)) ));
CommandItem.displayName = CommandPrimitive.Item.displayName CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({ const CommandShortcut = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLSpanElement>) => { }: React.HTMLAttributes<HTMLSpanElement>) => {
return ( return (
<span <span
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "ml-auto text-xs tracking-widest text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
) );
} };
CommandShortcut.displayName = "CommandShortcut" CommandShortcut.displayName = "CommandShortcut";
export { export {
Command, Command,
CommandDialog, CommandDialog,
CommandInput, CommandInput,
CommandList, CommandList,
CommandEmpty, CommandEmpty,
CommandGroup, CommandGroup,
CommandItem, CommandItem,
CommandShortcut, CommandShortcut,
CommandSeparator, CommandSeparator,
} };

View File

@@ -1,204 +1,204 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import { import {
CheckIcon, CheckIcon,
ChevronRightIcon, ChevronRightIcon,
DotFilledIcon, DotFilledIcon,
} from "@radix-ui/react-icons" } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const ContextMenu = ContextMenuPrimitive.Root const ContextMenu = ContextMenuPrimitive.Root;
const ContextMenuTrigger = ContextMenuPrimitive.Trigger const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
const ContextMenuGroup = ContextMenuPrimitive.Group const ContextMenuGroup = ContextMenuPrimitive.Group;
const ContextMenuPortal = ContextMenuPrimitive.Portal const ContextMenuPortal = ContextMenuPrimitive.Portal;
const ContextMenuSub = ContextMenuPrimitive.Sub const ContextMenuSub = ContextMenuPrimitive.Sub;
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
const ContextMenuSubTrigger = React.forwardRef< const ContextMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>, React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, children, ...props }, ref) => ( >(({ className, inset, children, ...props }, ref) => (
<ContextMenuPrimitive.SubTrigger <ContextMenuPrimitive.SubTrigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto h-4 w-4" /> <ChevronRightIcon className="ml-auto h-4 w-4" />
</ContextMenuPrimitive.SubTrigger> </ContextMenuPrimitive.SubTrigger>
)) ));
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
const ContextMenuSubContent = React.forwardRef< const ContextMenuSubContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubContent>, React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ContextMenuPrimitive.SubContent <ContextMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
const ContextMenuContent = React.forwardRef< const ContextMenuContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Content>, React.ElementRef<typeof ContextMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content <ContextMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
</ContextMenuPrimitive.Portal> </ContextMenuPrimitive.Portal>
)) ));
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
const ContextMenuItem = React.forwardRef< const ContextMenuItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Item>, React.ElementRef<typeof ContextMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Item <ContextMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
const ContextMenuCheckboxItem = React.forwardRef< const ContextMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>, React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => ( >(({ className, children, checked, ...props }, ref) => (
<ContextMenuPrimitive.CheckboxItem <ContextMenuPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator> <ContextMenuPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</ContextMenuPrimitive.ItemIndicator> </ContextMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</ContextMenuPrimitive.CheckboxItem> </ContextMenuPrimitive.CheckboxItem>
)) ));
ContextMenuCheckboxItem.displayName = ContextMenuCheckboxItem.displayName =
ContextMenuPrimitive.CheckboxItem.displayName ContextMenuPrimitive.CheckboxItem.displayName;
const ContextMenuRadioItem = React.forwardRef< const ContextMenuRadioItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>, React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem <ContextMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator> <ContextMenuPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current" /> <DotFilledIcon className="h-4 w-4 fill-current" />
</ContextMenuPrimitive.ItemIndicator> </ContextMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</ContextMenuPrimitive.RadioItem> </ContextMenuPrimitive.RadioItem>
)) ));
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
const ContextMenuLabel = React.forwardRef< const ContextMenuLabel = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Label>, React.ElementRef<typeof ContextMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Label <ContextMenuPrimitive.Label
ref={ref} ref={ref}
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground", "px-2 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
const ContextMenuSeparator = React.forwardRef< const ContextMenuSeparator = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Separator>, React.ElementRef<typeof ContextMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator <ContextMenuPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-border", className)} className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props} {...props}
/> />
)) ));
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
const ContextMenuShortcut = ({ const ContextMenuShortcut = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLSpanElement>) => { }: React.HTMLAttributes<HTMLSpanElement>) => {
return ( return (
<span <span
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "ml-auto text-xs tracking-widest text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
) );
} };
ContextMenuShortcut.displayName = "ContextMenuShortcut" ContextMenuShortcut.displayName = "ContextMenuShortcut";
export { export {
ContextMenu, ContextMenu,
ContextMenuTrigger, ContextMenuTrigger,
ContextMenuContent, ContextMenuContent,
ContextMenuItem, ContextMenuItem,
ContextMenuCheckboxItem, ContextMenuCheckboxItem,
ContextMenuRadioItem, ContextMenuRadioItem,
ContextMenuLabel, ContextMenuLabel,
ContextMenuSeparator, ContextMenuSeparator,
ContextMenuShortcut, ContextMenuShortcut,
ContextMenuGroup, ContextMenuGroup,
ContextMenuPortal, ContextMenuPortal,
ContextMenuSub, ContextMenuSub,
ContextMenuSubContent, ContextMenuSubContent,
ContextMenuSubTrigger, ContextMenuSubTrigger,
ContextMenuRadioGroup, ContextMenuRadioGroup,
} };

View File

@@ -1,53 +1,58 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
import { Copy, Check } from "lucide-react" import { Copy, Check } from "lucide-react";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
interface CopyButtonProps { interface CopyButtonProps {
textToCopy: string textToCopy: string;
} }
export default function CopyButton({ textToCopy }: CopyButtonProps) { export default function CopyButton({ textToCopy }: CopyButtonProps) {
const [isCopied, setIsCopied] = useState(false) const [isCopied, setIsCopied] = useState(false);
useEffect(() => { useEffect(() => {
if (isCopied) { if (isCopied) {
const timer = setTimeout(() => setIsCopied(false), 2000) const timer = setTimeout(() => setIsCopied(false), 2000);
return () => clearTimeout(timer) return () => clearTimeout(timer);
} }
}, [isCopied]) }, [isCopied]);
const handleCopy = async () => { const handleCopy = async () => {
try { try {
await navigator.clipboard.writeText(textToCopy) await navigator.clipboard.writeText(textToCopy);
setIsCopied(true) setIsCopied(true);
} catch (err) { } catch (err) {
console.error('Failed to copy text: ', err) console.error("Failed to copy text: ", err);
} }
} };
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="link" variant="link"
size="icon" size="icon"
onClick={handleCopy} onClick={handleCopy}
className="h-8 w-8" className="h-8 w-8"
> >
{isCopied ? ( {isCopied ? (
<Check className="h-4 w-4 " /> <Check className="h-4 w-4 " />
) : ( ) : (
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
)} )}
<span className="sr-only">Copy to clipboard</span> <span className="sr-only">Copy to clipboard</span>
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>{isCopied ? 'Copied!' : 'Copy to clipboard'}</p> <p>{isCopied ? "Copied!" : "Copy to clipboard"}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
) );
} }

View File

@@ -1,122 +1,122 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog" import * as DialogPrimitive from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons" import { Cross2Icon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Dialog = DialogPrimitive.Root const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef< const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>, React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
ref={ref} ref={ref}
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef< const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>, React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<DialogPortal> <DialogPortal>
<DialogOverlay /> <DialogOverlay />
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<Cross2Icon className="h-4 w-4" /> <Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</DialogPrimitive.Close> </DialogPrimitive.Close>
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DialogPortal> </DialogPortal>
)) ));
DialogContent.displayName = DialogPrimitive.Content.displayName DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({ const DialogHeader = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left", "flex flex-col space-y-1.5 text-center sm:text-left",
className className,
)} )}
{...props} {...props}
/> />
) );
DialogHeader.displayName = "DialogHeader" DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({ const DialogFooter = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
) );
DialogFooter.displayName = "DialogFooter" DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef< const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>, React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Title <DialogPrimitive.Title
ref={ref} ref={ref}
className={cn( className={cn(
"text-lg font-semibold leading-none tracking-tight", "text-lg font-semibold leading-none tracking-tight",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DialogTitle.displayName = DialogPrimitive.Title.displayName DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef< const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>, React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Description <DialogPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
DialogDescription.displayName = DialogPrimitive.Description.displayName DialogDescription.displayName = DialogPrimitive.Description.displayName;
export { export {
Dialog, Dialog,
DialogPortal, DialogPortal,
DialogOverlay, DialogOverlay,
DialogTrigger, DialogTrigger,
DialogClose, DialogClose,
DialogContent, DialogContent,
DialogHeader, DialogHeader,
DialogFooter, DialogFooter,
DialogTitle, DialogTitle,
DialogDescription, DialogDescription,
} };

View File

@@ -1,118 +1,118 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { Drawer as DrawerPrimitive } from "vaul" import { Drawer as DrawerPrimitive } from "vaul";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Drawer = ({ const Drawer = ({
shouldScaleBackground = true, shouldScaleBackground = true,
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => ( }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
<DrawerPrimitive.Root <DrawerPrimitive.Root
shouldScaleBackground={shouldScaleBackground} shouldScaleBackground={shouldScaleBackground}
{...props} {...props}
/> />
) );
Drawer.displayName = "Drawer" Drawer.displayName = "Drawer";
const DrawerTrigger = DrawerPrimitive.Trigger const DrawerTrigger = DrawerPrimitive.Trigger;
const DrawerPortal = DrawerPrimitive.Portal const DrawerPortal = DrawerPrimitive.Portal;
const DrawerClose = DrawerPrimitive.Close const DrawerClose = DrawerPrimitive.Close;
const DrawerOverlay = React.forwardRef< const DrawerOverlay = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Overlay>, React.ElementRef<typeof DrawerPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay> React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DrawerPrimitive.Overlay <DrawerPrimitive.Overlay
ref={ref} ref={ref}
className={cn("fixed inset-0 z-50 bg-black/80", className)} className={cn("fixed inset-0 z-50 bg-black/80", className)}
{...props} {...props}
/> />
)) ));
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
const DrawerContent = React.forwardRef< const DrawerContent = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Content>, React.ElementRef<typeof DrawerPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<DrawerPortal> <DrawerPortal>
<DrawerOverlay /> <DrawerOverlay />
<DrawerPrimitive.Content <DrawerPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background", "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
className className,
)} )}
{...props} {...props}
> >
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" /> <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
{children} {children}
</DrawerPrimitive.Content> </DrawerPrimitive.Content>
</DrawerPortal> </DrawerPortal>
)) ));
DrawerContent.displayName = "DrawerContent" DrawerContent.displayName = "DrawerContent";
const DrawerHeader = ({ const DrawerHeader = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
{...props} {...props}
/> />
) );
DrawerHeader.displayName = "DrawerHeader" DrawerHeader.displayName = "DrawerHeader";
const DrawerFooter = ({ const DrawerFooter = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn("mt-auto flex flex-col gap-2 p-4", className)} className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props} {...props}
/> />
) );
DrawerFooter.displayName = "DrawerFooter" DrawerFooter.displayName = "DrawerFooter";
const DrawerTitle = React.forwardRef< const DrawerTitle = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Title>, React.ElementRef<typeof DrawerPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DrawerPrimitive.Title <DrawerPrimitive.Title
ref={ref} ref={ref}
className={cn( className={cn(
"text-lg font-semibold leading-none tracking-tight", "text-lg font-semibold leading-none tracking-tight",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DrawerTitle.displayName = DrawerPrimitive.Title.displayName DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
const DrawerDescription = React.forwardRef< const DrawerDescription = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Description>, React.ElementRef<typeof DrawerPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DrawerPrimitive.Description <DrawerPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
DrawerDescription.displayName = DrawerPrimitive.Description.displayName DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
export { export {
Drawer, Drawer,
DrawerPortal, DrawerPortal,
DrawerOverlay, DrawerOverlay,
DrawerTrigger, DrawerTrigger,
DrawerClose, DrawerClose,
DrawerContent, DrawerContent,
DrawerHeader, DrawerHeader,
DrawerFooter, DrawerFooter,
DrawerTitle, DrawerTitle,
DrawerDescription, DrawerDescription,
} };

View File

@@ -1,205 +1,205 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { import {
CheckIcon, CheckIcon,
ChevronRightIcon, ChevronRightIcon,
DotFilledIcon, DotFilledIcon,
} from "@radix-ui/react-icons" } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const DropdownMenu = DropdownMenuPrimitive.Root const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSubTrigger = React.forwardRef< const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, children, ...props }, ref) => ( >(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent", "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto h-4 w-4" /> <ChevronRightIcon className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
)) ));
DropdownMenuSubTrigger.displayName = DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef< const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuSubContent.displayName = DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName DropdownMenuPrimitive.SubContent.displayName;
const DropdownMenuContent = React.forwardRef< const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>, React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => ( >(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
ref={ref} ref={ref}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
)) ));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef< const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>, React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
const DropdownMenuCheckboxItem = React.forwardRef< const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => ( >(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>
)) ));
DropdownMenuCheckboxItem.displayName = DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef< const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current" /> <DotFilledIcon className="h-4 w-4 fill-current" />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>
)) ));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
const DropdownMenuLabel = React.forwardRef< const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>, React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label <DropdownMenuPrimitive.Label
ref={ref} ref={ref}
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
const DropdownMenuSeparator = React.forwardRef< const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>, React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)} className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props} {...props}
/> />
)) ));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({ const DropdownMenuShortcut = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLSpanElement>) => { }: React.HTMLAttributes<HTMLSpanElement>) => {
return ( return (
<span <span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)} className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props} {...props}
/> />
) );
} };
DropdownMenuShortcut.displayName = "DropdownMenuShortcut" DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export { export {
DropdownMenu, DropdownMenu,
DropdownMenuTrigger, DropdownMenuTrigger,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuRadioItem, DropdownMenuRadioItem,
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuShortcut, DropdownMenuShortcut,
DropdownMenuGroup, DropdownMenuGroup,
DropdownMenuPortal, DropdownMenuPortal,
DropdownMenuSub, DropdownMenuSub,
DropdownMenuSubContent, DropdownMenuSubContent,
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
} };

View File

@@ -1,178 +1,179 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label" import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { import {
Controller, Controller,
ControllerProps, ControllerProps,
FieldPath, FieldPath,
FieldValues, FieldValues,
FormProvider, FormProvider,
useFormContext, useFormContext,
} from "react-hook-form" } from "react-hook-form";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label";
const Form = FormProvider const Form = FormProvider;
type FormFieldContextValue< type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues, TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = { > = {
name: TName name: TName;
} };
const FormFieldContext = React.createContext<FormFieldContextValue>( const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue {} as FormFieldContextValue,
) );
const FormField = < const FormField = <
TFieldValues extends FieldValues = FieldValues, TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({ >({
...props ...props
}: ControllerProps<TFieldValues, TName>) => { }: ControllerProps<TFieldValues, TName>) => {
return ( return (
<FormFieldContext.Provider value={{ name: props.name }}> <FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} /> <Controller {...props} />
</FormFieldContext.Provider> </FormFieldContext.Provider>
) );
} };
const useFormField = () => { const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext) const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext) const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext() const { getFieldState, formState } = useFormContext();
const fieldState = getFieldState(fieldContext.name, formState) const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) { if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>") throw new Error("useFormField should be used within <FormField>");
} }
const { id } = itemContext const { id } = itemContext;
return { return {
id, id,
name: fieldContext.name, name: fieldContext.name,
formItemId: `${id}-form-item`, formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`, formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`, formMessageId: `${id}-form-item-message`,
...fieldState, ...fieldState,
} };
} };
type FormItemContextValue = { type FormItemContextValue = {
id: string id: string;
} };
const FormItemContext = React.createContext<FormItemContextValue>( const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue {} as FormItemContextValue,
) );
const FormItem = React.forwardRef< const FormItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
const id = React.useId() const id = React.useId();
return ( return (
<FormItemContext.Provider value={{ id }}> <FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} /> <div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider> </FormItemContext.Provider>
) );
}) });
FormItem.displayName = "FormItem" FormItem.displayName = "FormItem";
const FormLabel = React.forwardRef< const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>, React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField() const { error, formItemId } = useFormField();
return ( return (
<Label <Label
ref={ref} ref={ref}
className={cn(error && "text-destructive", className)} className={cn(error && "text-destructive", className)}
htmlFor={formItemId} htmlFor={formItemId}
{...props} {...props}
/> />
) );
}) });
FormLabel.displayName = "FormLabel" FormLabel.displayName = "FormLabel";
const FormControl = React.forwardRef< const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>, React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot> React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => { >(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField() const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
return ( return (
<Slot <Slot
ref={ref} ref={ref}
id={formItemId} id={formItemId}
aria-describedby={ aria-describedby={
!error !error
? `${formDescriptionId}` ? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}` : `${formDescriptionId} ${formMessageId}`
} }
aria-invalid={!!error} aria-invalid={!!error}
{...props} {...props}
/> />
) );
}) });
FormControl.displayName = "FormControl" FormControl.displayName = "FormControl";
const FormDescription = React.forwardRef< const FormDescription = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement> React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField() const { formDescriptionId } = useFormField();
return ( return (
<p <p
ref={ref} ref={ref}
id={formDescriptionId} id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)} className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props} {...props}
/> />
) );
}) });
FormDescription.displayName = "FormDescription" FormDescription.displayName = "FormDescription";
const FormMessage = React.forwardRef< const FormMessage = React.forwardRef<
HTMLParagraphElement, HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement> React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => { >(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField() const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children const body = error ? String(error?.message) : children;
if (!body) { if (!body) {
return null return null;
} }
return ( return (
<p <p
ref={ref} ref={ref}
id={formMessageId} id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)} className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props} {...props}
> >
{body} {body}
</p> </p>
) );
}) });
FormMessage.displayName = "FormMessage" FormMessage.displayName = "FormMessage";
export { export {
useFormField, useFormField,
Form, Form,
FormItem, FormItem,
FormLabel, FormLabel,
FormControl, FormControl,
FormDescription, FormDescription,
FormMessage, FormMessage,
FormField, FormField,
} };

View File

@@ -1,29 +1,29 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as HoverCardPrimitive from "@radix-ui/react-hover-card" import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const HoverCard = HoverCardPrimitive.Root const HoverCard = HoverCardPrimitive.Root;
const HoverCardTrigger = HoverCardPrimitive.Trigger const HoverCardTrigger = HoverCardPrimitive.Trigger;
const HoverCardContent = React.forwardRef< const HoverCardContent = React.forwardRef<
React.ElementRef<typeof HoverCardPrimitive.Content>, React.ElementRef<typeof HoverCardPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<HoverCardPrimitive.Content <HoverCardPrimitive.Content
ref={ref} ref={ref}
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
export { HoverCard, HoverCardTrigger, HoverCardContent } export { HoverCard, HoverCardTrigger, HoverCardContent };

View File

@@ -1,71 +1,71 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { DashIcon } from "@radix-ui/react-icons" import { DashIcon } from "@radix-ui/react-icons";
import { OTPInput, OTPInputContext } from "input-otp" import { OTPInput, OTPInputContext } from "input-otp";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const InputOTP = React.forwardRef< const InputOTP = React.forwardRef<
React.ElementRef<typeof OTPInput>, React.ElementRef<typeof OTPInput>,
React.ComponentPropsWithoutRef<typeof OTPInput> React.ComponentPropsWithoutRef<typeof OTPInput>
>(({ className, containerClassName, ...props }, ref) => ( >(({ className, containerClassName, ...props }, ref) => (
<OTPInput <OTPInput
ref={ref} ref={ref}
containerClassName={cn( containerClassName={cn(
"flex items-center gap-2 has-[:disabled]:opacity-50", "flex items-center gap-2 has-[:disabled]:opacity-50",
containerClassName containerClassName,
)} )}
className={cn("disabled:cursor-not-allowed", className)} className={cn("disabled:cursor-not-allowed", className)}
{...props} {...props}
/> />
)) ));
InputOTP.displayName = "InputOTP" InputOTP.displayName = "InputOTP";
const InputOTPGroup = React.forwardRef< const InputOTPGroup = React.forwardRef<
React.ElementRef<"div">, React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div"> React.ComponentPropsWithoutRef<"div">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center", className)} {...props} /> <div ref={ref} className={cn("flex items-center", className)} {...props} />
)) ));
InputOTPGroup.displayName = "InputOTPGroup" InputOTPGroup.displayName = "InputOTPGroup";
const InputOTPSlot = React.forwardRef< const InputOTPSlot = React.forwardRef<
React.ElementRef<"div">, React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div"> & { index: number } React.ComponentPropsWithoutRef<"div"> & { index: number }
>(({ index, className, ...props }, ref) => { >(({ index, className, ...props }, ref) => {
const inputOTPContext = React.useContext(OTPInputContext) const inputOTPContext = React.useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
return ( return (
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md", "relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
isActive && "z-10 ring-1 ring-ring", isActive && "z-10 ring-1 ring-ring",
className className,
)} )}
{...props} {...props}
> >
{char} {char}
{hasFakeCaret && ( {hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" /> <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
</div> </div>
)} )}
</div> </div>
) );
}) });
InputOTPSlot.displayName = "InputOTPSlot" InputOTPSlot.displayName = "InputOTPSlot";
const InputOTPSeparator = React.forwardRef< const InputOTPSeparator = React.forwardRef<
React.ElementRef<"div">, React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div"> React.ComponentPropsWithoutRef<"div">
>(({ ...props }, ref) => ( >(({ ...props }, ref) => (
<div ref={ref} role="separator" {...props}> <div ref={ref} role="separator" {...props}>
<DashIcon /> <DashIcon />
</div> </div>
)) ));
InputOTPSeparator.displayName = "InputOTPSeparator" InputOTPSeparator.displayName = "InputOTPSeparator";
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };

View File

@@ -1,25 +1,25 @@
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
export interface InputProps export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {} extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>( const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => { ({ className, type, ...props }, ref) => {
return ( return (
<input <input
type={type} type={type}
className={cn( className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
) );
} },
) );
Input.displayName = "Input" Input.displayName = "Input";
export { Input } export { Input };

View File

@@ -1,26 +1,26 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label" import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const labelVariants = cva( const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
) );
const Label = React.forwardRef< const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>, React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants> VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<LabelPrimitive.Root <LabelPrimitive.Root
ref={ref} ref={ref}
className={cn(labelVariants(), className)} className={cn(labelVariants(), className)}
{...props} {...props}
/> />
)) ));
Label.displayName = LabelPrimitive.Root.displayName Label.displayName = LabelPrimitive.Root.displayName;
export { Label } export { Label };

View File

@@ -1,240 +1,240 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { import {
CheckIcon, CheckIcon,
ChevronRightIcon, ChevronRightIcon,
DotFilledIcon, DotFilledIcon,
} from "@radix-ui/react-icons" } from "@radix-ui/react-icons";
import * as MenubarPrimitive from "@radix-ui/react-menubar" import * as MenubarPrimitive from "@radix-ui/react-menubar";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const MenubarMenu = MenubarPrimitive.Menu const MenubarMenu = MenubarPrimitive.Menu;
const MenubarGroup = MenubarPrimitive.Group const MenubarGroup = MenubarPrimitive.Group;
const MenubarPortal = MenubarPrimitive.Portal const MenubarPortal = MenubarPrimitive.Portal;
const MenubarSub = MenubarPrimitive.Sub const MenubarSub = MenubarPrimitive.Sub;
const MenubarRadioGroup = MenubarPrimitive.RadioGroup const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
const Menubar = React.forwardRef< const Menubar = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Root>, React.ElementRef<typeof MenubarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<MenubarPrimitive.Root <MenubarPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm", "flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm",
className className,
)} )}
{...props} {...props}
/> />
)) ));
Menubar.displayName = MenubarPrimitive.Root.displayName Menubar.displayName = MenubarPrimitive.Root.displayName;
const MenubarTrigger = React.forwardRef< const MenubarTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Trigger>, React.ElementRef<typeof MenubarPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<MenubarPrimitive.Trigger <MenubarPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", "flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
className className,
)} )}
{...props} {...props}
/> />
)) ));
MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
const MenubarSubTrigger = React.forwardRef< const MenubarSubTrigger = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubTrigger>, React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & { React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, children, ...props }, ref) => ( >(({ className, inset, children, ...props }, ref) => (
<MenubarPrimitive.SubTrigger <MenubarPrimitive.SubTrigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto h-4 w-4" /> <ChevronRightIcon className="ml-auto h-4 w-4" />
</MenubarPrimitive.SubTrigger> </MenubarPrimitive.SubTrigger>
)) ));
MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
const MenubarSubContent = React.forwardRef< const MenubarSubContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.SubContent>, React.ElementRef<typeof MenubarPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<MenubarPrimitive.SubContent <MenubarPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
const MenubarContent = React.forwardRef< const MenubarContent = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Content>, React.ElementRef<typeof MenubarPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>
>( >(
( (
{ className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, { className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
ref ref,
) => ( ) => (
<MenubarPrimitive.Portal> <MenubarPrimitive.Portal>
<MenubarPrimitive.Content <MenubarPrimitive.Content
ref={ref} ref={ref}
align={align} align={align}
alignOffset={alignOffset} alignOffset={alignOffset}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
</MenubarPrimitive.Portal> </MenubarPrimitive.Portal>
) ),
) );
MenubarContent.displayName = MenubarPrimitive.Content.displayName MenubarContent.displayName = MenubarPrimitive.Content.displayName;
const MenubarItem = React.forwardRef< const MenubarItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Item>, React.ElementRef<typeof MenubarPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & { React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Item <MenubarPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
MenubarItem.displayName = MenubarPrimitive.Item.displayName MenubarItem.displayName = MenubarPrimitive.Item.displayName;
const MenubarCheckboxItem = React.forwardRef< const MenubarCheckboxItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.CheckboxItem>, React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => ( >(({ className, children, checked, ...props }, ref) => (
<MenubarPrimitive.CheckboxItem <MenubarPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator> <MenubarPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</MenubarPrimitive.ItemIndicator> </MenubarPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</MenubarPrimitive.CheckboxItem> </MenubarPrimitive.CheckboxItem>
)) ));
MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName;
const MenubarRadioItem = React.forwardRef< const MenubarRadioItem = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.RadioItem>, React.ElementRef<typeof MenubarPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<MenubarPrimitive.RadioItem <MenubarPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator> <MenubarPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current" /> <DotFilledIcon className="h-4 w-4 fill-current" />
</MenubarPrimitive.ItemIndicator> </MenubarPrimitive.ItemIndicator>
</span> </span>
{children} {children}
</MenubarPrimitive.RadioItem> </MenubarPrimitive.RadioItem>
)) ));
MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName;
const MenubarLabel = React.forwardRef< const MenubarLabel = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Label>, React.ElementRef<typeof MenubarPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & { React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
inset?: boolean inset?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, ...props }, ref) => (
<MenubarPrimitive.Label <MenubarPrimitive.Label
ref={ref} ref={ref}
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
)) ));
MenubarLabel.displayName = MenubarPrimitive.Label.displayName MenubarLabel.displayName = MenubarPrimitive.Label.displayName;
const MenubarSeparator = React.forwardRef< const MenubarSeparator = React.forwardRef<
React.ElementRef<typeof MenubarPrimitive.Separator>, React.ElementRef<typeof MenubarPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator> React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<MenubarPrimitive.Separator <MenubarPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)} className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props} {...props}
/> />
)) ));
MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName;
const MenubarShortcut = ({ const MenubarShortcut = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLSpanElement>) => { }: React.HTMLAttributes<HTMLSpanElement>) => {
return ( return (
<span <span
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "ml-auto text-xs tracking-widest text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
) );
} };
MenubarShortcut.displayname = "MenubarShortcut" MenubarShortcut.displayname = "MenubarShortcut";
export { export {
Menubar, Menubar,
MenubarMenu, MenubarMenu,
MenubarTrigger, MenubarTrigger,
MenubarContent, MenubarContent,
MenubarItem, MenubarItem,
MenubarSeparator, MenubarSeparator,
MenubarLabel, MenubarLabel,
MenubarCheckboxItem, MenubarCheckboxItem,
MenubarRadioGroup, MenubarRadioGroup,
MenubarRadioItem, MenubarRadioItem,
MenubarPortal, MenubarPortal,
MenubarSubContent, MenubarSubContent,
MenubarSubTrigger, MenubarSubTrigger,
MenubarGroup, MenubarGroup,
MenubarSub, MenubarSub,
MenubarShortcut, MenubarShortcut,
} };

View File

@@ -1,128 +1,128 @@
import * as React from "react" import * as React from "react";
import { ChevronDownIcon } from "@radix-ui/react-icons" import { ChevronDownIcon } from "@radix-ui/react-icons";
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const NavigationMenu = React.forwardRef< const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>, React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root <NavigationMenuPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center", "relative z-10 flex max-w-max flex-1 items-center justify-center",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<NavigationMenuViewport /> <NavigationMenuViewport />
</NavigationMenuPrimitive.Root> </NavigationMenuPrimitive.Root>
)) ));
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
const NavigationMenuList = React.forwardRef< const NavigationMenuList = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.List>, React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List <NavigationMenuPrimitive.List
ref={ref} ref={ref}
className={cn( className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1", "group flex flex-1 list-none items-center justify-center space-x-1",
className className,
)} )}
{...props} {...props}
/> />
)) ));
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
const NavigationMenuItem = NavigationMenuPrimitive.Item const NavigationMenuItem = NavigationMenuPrimitive.Item;
const navigationMenuTriggerStyle = cva( const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50" "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50",
) );
const NavigationMenuTrigger = React.forwardRef< const NavigationMenuTrigger = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>, React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger <NavigationMenuPrimitive.Trigger
ref={ref} ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)} className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props} {...props}
> >
{children}{" "} {children}{" "}
<ChevronDownIcon <ChevronDownIcon
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180" className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
aria-hidden="true" aria-hidden="true"
/> />
</NavigationMenuPrimitive.Trigger> </NavigationMenuPrimitive.Trigger>
)) ));
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
const NavigationMenuContent = React.forwardRef< const NavigationMenuContent = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Content>, React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content <NavigationMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ", "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className className,
)} )}
{...props} {...props}
/> />
)) ));
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
const NavigationMenuLink = NavigationMenuPrimitive.Link const NavigationMenuLink = NavigationMenuPrimitive.Link;
const NavigationMenuViewport = React.forwardRef< const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>, React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}> <div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport <NavigationMenuPrimitive.Viewport
className={cn( className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]", "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
</div> </div>
)) ));
NavigationMenuViewport.displayName = NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName NavigationMenuPrimitive.Viewport.displayName;
const NavigationMenuIndicator = React.forwardRef< const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>, React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator <NavigationMenuPrimitive.Indicator
ref={ref} ref={ref}
className={cn( className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className className,
)} )}
{...props} {...props}
> >
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator> </NavigationMenuPrimitive.Indicator>
)) ));
NavigationMenuIndicator.displayName = NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName NavigationMenuPrimitive.Indicator.displayName;
export { export {
navigationMenuTriggerStyle, navigationMenuTriggerStyle,
NavigationMenu, NavigationMenu,
NavigationMenuList, NavigationMenuList,
NavigationMenuItem, NavigationMenuItem,
NavigationMenuContent, NavigationMenuContent,
NavigationMenuTrigger, NavigationMenuTrigger,
NavigationMenuLink, NavigationMenuLink,
NavigationMenuIndicator, NavigationMenuIndicator,
NavigationMenuViewport, NavigationMenuViewport,
} };

View File

@@ -1,121 +1,121 @@
import * as React from "react" import * as React from "react";
import { import {
ChevronLeftIcon, ChevronLeftIcon,
ChevronRightIcon, ChevronRightIcon,
DotsHorizontalIcon, DotsHorizontalIcon,
} from "@radix-ui/react-icons" } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { ButtonProps, buttonVariants } from "@/components/ui/button" import { ButtonProps, buttonVariants } from "@/components/ui/button";
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav <nav
role="navigation" role="navigation"
aria-label="pagination" aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)} className={cn("mx-auto flex w-full justify-center", className)}
{...props} {...props}
/> />
) );
Pagination.displayName = "Pagination" Pagination.displayName = "Pagination";
const PaginationContent = React.forwardRef< const PaginationContent = React.forwardRef<
HTMLUListElement, HTMLUListElement,
React.ComponentProps<"ul"> React.ComponentProps<"ul">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ul <ul
ref={ref} ref={ref}
className={cn("flex flex-row items-center gap-1", className)} className={cn("flex flex-row items-center gap-1", className)}
{...props} {...props}
/> />
)) ));
PaginationContent.displayName = "PaginationContent" PaginationContent.displayName = "PaginationContent";
const PaginationItem = React.forwardRef< const PaginationItem = React.forwardRef<
HTMLLIElement, HTMLLIElement,
React.ComponentProps<"li"> React.ComponentProps<"li">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} /> <li ref={ref} className={cn("", className)} {...props} />
)) ));
PaginationItem.displayName = "PaginationItem" PaginationItem.displayName = "PaginationItem";
type PaginationLinkProps = { type PaginationLinkProps = {
isActive?: boolean isActive?: boolean;
} & Pick<ButtonProps, "size"> & } & Pick<ButtonProps, "size"> &
React.ComponentProps<"a"> React.ComponentProps<"a">;
const PaginationLink = ({ const PaginationLink = ({
className, className,
isActive, isActive,
size = "icon", size = "icon",
...props ...props
}: PaginationLinkProps) => ( }: PaginationLinkProps) => (
<a <a
aria-current={isActive ? "page" : undefined} aria-current={isActive ? "page" : undefined}
className={cn( className={cn(
buttonVariants({ buttonVariants({
variant: isActive ? "outline" : "ghost", variant: isActive ? "outline" : "ghost",
size, size,
}), }),
className className,
)} )}
{...props} {...props}
/> />
) );
PaginationLink.displayName = "PaginationLink" PaginationLink.displayName = "PaginationLink";
const PaginationPrevious = ({ const PaginationPrevious = ({
className, className,
...props ...props
}: React.ComponentProps<typeof PaginationLink>) => ( }: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink <PaginationLink
aria-label="Go to previous page" aria-label="Go to previous page"
size="default" size="default"
className={cn("gap-1 pl-2.5", className)} className={cn("gap-1 pl-2.5", className)}
{...props} {...props}
> >
<ChevronLeftIcon className="h-4 w-4" /> <ChevronLeftIcon className="h-4 w-4" />
<span>Previous</span> <span>Previous</span>
</PaginationLink> </PaginationLink>
) );
PaginationPrevious.displayName = "PaginationPrevious" PaginationPrevious.displayName = "PaginationPrevious";
const PaginationNext = ({ const PaginationNext = ({
className, className,
...props ...props
}: React.ComponentProps<typeof PaginationLink>) => ( }: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink <PaginationLink
aria-label="Go to next page" aria-label="Go to next page"
size="default" size="default"
className={cn("gap-1 pr-2.5", className)} className={cn("gap-1 pr-2.5", className)}
{...props} {...props}
> >
<span>Next</span> <span>Next</span>
<ChevronRightIcon className="h-4 w-4" /> <ChevronRightIcon className="h-4 w-4" />
</PaginationLink> </PaginationLink>
) );
PaginationNext.displayName = "PaginationNext" PaginationNext.displayName = "PaginationNext";
const PaginationEllipsis = ({ const PaginationEllipsis = ({
className, className,
...props ...props
}: React.ComponentProps<"span">) => ( }: React.ComponentProps<"span">) => (
<span <span
aria-hidden aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)} className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props} {...props}
> >
<DotsHorizontalIcon className="h-4 w-4" /> <DotsHorizontalIcon className="h-4 w-4" />
<span className="sr-only">More pages</span> <span className="sr-only">More pages</span>
</span> </span>
) );
PaginationEllipsis.displayName = "PaginationEllipsis" PaginationEllipsis.displayName = "PaginationEllipsis";
export { export {
Pagination, Pagination,
PaginationContent, PaginationContent,
PaginationLink, PaginationLink,
PaginationItem, PaginationItem,
PaginationPrevious, PaginationPrevious,
PaginationNext, PaginationNext,
PaginationEllipsis, PaginationEllipsis,
} };

View File

@@ -8,39 +8,39 @@ import { Input, type InputProps } from "@/components/ui/input";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>( const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
const [showPassword, setShowPassword] = React.useState(false); const [showPassword, setShowPassword] = React.useState(false);
const disabled = const disabled =
props.value === "" || props.value === undefined || props.disabled; props.value === "" || props.value === undefined || props.disabled;
return ( return (
<div className="relative"> <div className="relative">
<Input <Input
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
className={cn("hide-password-toggle pr-10", className)} className={cn("hide-password-toggle pr-10", className)}
ref={ref} ref={ref}
{...props} {...props}
/> />
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
size="sm" size="sm"
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent" className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
onClick={() => setShowPassword((prev) => !prev)} onClick={() => setShowPassword((prev) => !prev)}
disabled={disabled} disabled={disabled}
> >
{showPassword && !disabled ? ( {showPassword && !disabled ? (
<EyeIcon className="h-4 w-4" aria-hidden="true" /> <EyeIcon className="h-4 w-4" aria-hidden="true" />
) : ( ) : (
<EyeOffIcon className="h-4 w-4" aria-hidden="true" /> <EyeOffIcon className="h-4 w-4" aria-hidden="true" />
)} )}
<span className="sr-only"> <span className="sr-only">
{showPassword ? "Hide password" : "Show password"} {showPassword ? "Hide password" : "Show password"}
</span> </span>
</Button> </Button>
{/* hides browsers password toggles */} {/* hides browsers password toggles */}
<style>{` <style>{`
.hide-password-toggle::-ms-reveal, .hide-password-toggle::-ms-reveal,
.hide-password-toggle::-ms-clear { .hide-password-toggle::-ms-clear {
visibility: hidden; visibility: hidden;
@@ -48,9 +48,9 @@ const PasswordInput = React.forwardRef<HTMLInputElement, InputProps>(
display: none; display: none;
} }
`}</style> `}</style>
</div> </div>
); );
} },
); );
PasswordInput.displayName = "PasswordInput"; PasswordInput.displayName = "PasswordInput";

View File

@@ -1,33 +1,33 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover" import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Popover = PopoverPrimitive.Root const Popover = PopoverPrimitive.Root;
const PopoverTrigger = PopoverPrimitive.Trigger const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverContent = React.forwardRef< const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>, React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal> <PopoverPrimitive.Portal>
<PopoverPrimitive.Content <PopoverPrimitive.Content
ref={ref} ref={ref}
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
</PopoverPrimitive.Portal> </PopoverPrimitive.Portal>
)) ));
PopoverContent.displayName = PopoverPrimitive.Content.displayName PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };

View File

@@ -1,28 +1,28 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ProgressPrimitive from "@radix-ui/react-progress" import * as ProgressPrimitive from "@radix-ui/react-progress";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Progress = React.forwardRef< const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>, React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => ( >(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root <ProgressPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20", "relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
className className,
)} )}
{...props} {...props}
> >
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all" className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>
)) ));
Progress.displayName = ProgressPrimitive.Root.displayName Progress.displayName = ProgressPrimitive.Root.displayName;
export { Progress } export { Progress };

View File

@@ -1,44 +1,44 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { CheckIcon } from "@radix-ui/react-icons" import { CheckIcon } from "@radix-ui/react-icons";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const RadioGroup = React.forwardRef< const RadioGroup = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Root>, React.ElementRef<typeof RadioGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
return ( return (
<RadioGroupPrimitive.Root <RadioGroupPrimitive.Root
className={cn("grid gap-2", className)} className={cn("grid gap-2", className)}
{...props} {...props}
ref={ref} ref={ref}
/> />
) );
}) });
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
const RadioGroupItem = React.forwardRef< const RadioGroupItem = React.forwardRef<
React.ElementRef<typeof RadioGroupPrimitive.Item>, React.ElementRef<typeof RadioGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
>(({ className, ...props }, ref) => { >(({ className, ...props }, ref) => {
return ( return (
<RadioGroupPrimitive.Item <RadioGroupPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", "aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<RadioGroupPrimitive.Indicator className="flex items-center justify-center"> <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<CheckIcon className="h-3.5 w-3.5 fill-primary" /> <CheckIcon className="h-3.5 w-3.5 fill-primary" />
</RadioGroupPrimitive.Indicator> </RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item> </RadioGroupPrimitive.Item>
) );
}) });
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
export { RadioGroup, RadioGroupItem } export { RadioGroup, RadioGroupItem };

View File

@@ -1,45 +1,45 @@
"use client" "use client";
import { DragHandleDots2Icon } from "@radix-ui/react-icons" import { DragHandleDots2Icon } from "@radix-ui/react-icons";
import * as ResizablePrimitive from "react-resizable-panels" import * as ResizablePrimitive from "react-resizable-panels";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const ResizablePanelGroup = ({ const ResizablePanelGroup = ({
className, className,
...props ...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => ( }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup <ResizablePrimitive.PanelGroup
className={cn( className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col", "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className className,
)} )}
{...props} {...props}
/> />
) );
const ResizablePanel = ResizablePrimitive.Panel const ResizablePanel = ResizablePrimitive.Panel;
const ResizableHandle = ({ const ResizableHandle = ({
withHandle, withHandle,
className, className,
...props ...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & { }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
withHandle?: boolean withHandle?: boolean;
}) => ( }) => (
<ResizablePrimitive.PanelResizeHandle <ResizablePrimitive.PanelResizeHandle
className={cn( className={cn(
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className className,
)} )}
{...props} {...props}
> >
{withHandle && ( {withHandle && (
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border"> <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<DragHandleDots2Icon className="h-2.5 w-2.5" /> <DragHandleDots2Icon className="h-2.5 w-2.5" />
</div> </div>
)} )}
</ResizablePrimitive.PanelResizeHandle> </ResizablePrimitive.PanelResizeHandle>
) );
export { ResizablePanelGroup, ResizablePanel, ResizableHandle } export { ResizablePanelGroup, ResizablePanel, ResizableHandle };

View File

@@ -1,48 +1,48 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const ScrollArea = React.forwardRef< const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>, React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root <ScrollAreaPrimitive.Root
ref={ref} ref={ref}
className={cn("relative overflow-hidden", className)} className={cn("relative overflow-hidden", className)}
{...props} {...props}
> >
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
<ScrollBar /> <ScrollBar />
<ScrollAreaPrimitive.Corner /> <ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root> </ScrollAreaPrimitive.Root>
)) ));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef< const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => ( >(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar <ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref} ref={ref}
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"flex touch-none select-none transition-colors", "flex touch-none select-none transition-colors",
orientation === "vertical" && orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]", "h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" && orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]", "h-2.5 flex-col border-t border-t-transparent p-[1px]",
className className,
)} )}
{...props} {...props}
> >
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar> </ScrollAreaPrimitive.ScrollAreaScrollbar>
)) ));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar } export { ScrollArea, ScrollBar };

View File

@@ -1,164 +1,164 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { import {
CaretSortIcon, CaretSortIcon,
CheckIcon, CheckIcon,
ChevronDownIcon, ChevronDownIcon,
ChevronUpIcon, ChevronUpIcon,
} from "@radix-ui/react-icons" } from "@radix-ui/react-icons";
import * as SelectPrimitive from "@radix-ui/react-select" import * as SelectPrimitive from "@radix-ui/react-select";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Select = SelectPrimitive.Root const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef< const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>, React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<SelectPrimitive.Icon asChild> <SelectPrimitive.Icon asChild>
<CaretSortIcon className="h-4 w-4 opacity-50" /> <CaretSortIcon className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon> </SelectPrimitive.Icon>
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>
)) ));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef< const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton <SelectPrimitive.ScrollUpButton
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default items-center justify-center py-1", "flex cursor-default items-center justify-center py-1",
className className,
)} )}
{...props} {...props}
> >
<ChevronUpIcon /> <ChevronUpIcon />
</SelectPrimitive.ScrollUpButton> </SelectPrimitive.ScrollUpButton>
)) ));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef< const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton <SelectPrimitive.ScrollDownButton
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default items-center justify-center py-1", "flex cursor-default items-center justify-center py-1",
className className,
)} )}
{...props} {...props}
> >
<ChevronDownIcon /> <ChevronDownIcon />
</SelectPrimitive.ScrollDownButton> </SelectPrimitive.ScrollDownButton>
)) ));
SelectScrollDownButton.displayName = SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef< const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>, React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => ( >(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal> <SelectPrimitive.Portal>
<SelectPrimitive.Content <SelectPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" && position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className className,
)} )}
position={position} position={position}
{...props} {...props}
> >
<SelectScrollUpButton /> <SelectScrollUpButton />
<SelectPrimitive.Viewport <SelectPrimitive.Viewport
className={cn( className={cn(
"p-1", "p-1",
position === "popper" && position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)} )}
> >
{children} {children}
</SelectPrimitive.Viewport> </SelectPrimitive.Viewport>
<SelectScrollDownButton /> <SelectScrollDownButton />
</SelectPrimitive.Content> </SelectPrimitive.Content>
</SelectPrimitive.Portal> </SelectPrimitive.Portal>
)) ));
SelectContent.displayName = SelectPrimitive.Content.displayName SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef< const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>, React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SelectPrimitive.Label <SelectPrimitive.Label
ref={ref} ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", className)} className={cn("px-2 py-1.5 text-sm font-semibold", className)}
{...props} {...props}
/> />
)) ));
SelectLabel.displayName = SelectPrimitive.Label.displayName SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef< const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>, React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item <SelectPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator> <SelectPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
</span> </span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item> </SelectPrimitive.Item>
)) ));
SelectItem.displayName = SelectPrimitive.Item.displayName SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef< const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>, React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SelectPrimitive.Separator <SelectPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)} className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props} {...props}
/> />
)) ));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export { export {
Select, Select,
SelectGroup, SelectGroup,
SelectValue, SelectValue,
SelectTrigger, SelectTrigger,
SelectContent, SelectContent,
SelectLabel, SelectLabel,
SelectItem, SelectItem,
SelectSeparator, SelectSeparator,
SelectScrollUpButton, SelectScrollUpButton,
SelectScrollDownButton, SelectScrollDownButton,
} };

View File

@@ -1,31 +1,31 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator" import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Separator = React.forwardRef< const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>, React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>( >(
( (
{ className, orientation = "horizontal", decorative = true, ...props }, { className, orientation = "horizontal", decorative = true, ...props },
ref ref,
) => ( ) => (
<SeparatorPrimitive.Root <SeparatorPrimitive.Root
ref={ref} ref={ref}
decorative={decorative} decorative={decorative}
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"shrink-0 bg-border", "shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className className,
)} )}
{...props} {...props}
/> />
) ),
) );
Separator.displayName = SeparatorPrimitive.Root.displayName Separator.displayName = SeparatorPrimitive.Root.displayName;
export { Separator } export { Separator };

View File

@@ -1,140 +1,140 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog" import * as SheetPrimitive from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons" import { Cross2Icon } from "@radix-ui/react-icons";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Sheet = SheetPrimitive.Root const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef< const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>, React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
/> />
)) ));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva( const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
{ {
variants: { variants: {
side: { side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom: bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right: right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
}, },
}, },
defaultVariants: { defaultVariants: {
side: "right", side: "right",
}, },
} },
) );
interface SheetContentProps interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {} VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef< const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>, React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => ( >(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal> <SheetPortal>
<SheetOverlay /> <SheetOverlay />
<SheetPrimitive.Content <SheetPrimitive.Content
ref={ref} ref={ref}
className={cn(sheetVariants({ side }), className)} className={cn(sheetVariants({ side }), className)}
{...props} {...props}
> >
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<Cross2Icon className="h-4 w-4" /> <Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</SheetPrimitive.Close> </SheetPrimitive.Close>
{children} {children}
</SheetPrimitive.Content> </SheetPrimitive.Content>
</SheetPortal> </SheetPortal>
)) ));
SheetContent.displayName = SheetPrimitive.Content.displayName SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({ const SheetHeader = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col space-y-2 text-center sm:text-left", "flex flex-col space-y-2 text-center sm:text-left",
className className,
)} )}
{...props} {...props}
/> />
) );
SheetHeader.displayName = "SheetHeader" SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({ const SheetFooter = ({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
) );
SheetFooter.displayName = "SheetFooter" SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef< const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>, React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Title <SheetPrimitive.Title
ref={ref} ref={ref}
className={cn("text-lg font-semibold text-foreground", className)} className={cn("text-lg font-semibold text-foreground", className)}
{...props} {...props}
/> />
)) ));
SheetTitle.displayName = SheetPrimitive.Title.displayName SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef< const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>, React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Description <SheetPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
SheetDescription.displayName = SheetPrimitive.Description.displayName SheetDescription.displayName = SheetPrimitive.Description.displayName;
export { export {
Sheet, Sheet,
SheetPortal, SheetPortal,
SheetOverlay, SheetOverlay,
SheetTrigger, SheetTrigger,
SheetClose, SheetClose,
SheetContent, SheetContent,
SheetHeader, SheetHeader,
SheetFooter, SheetFooter,
SheetTitle, SheetTitle,
SheetDescription, SheetDescription,
} };

View File

@@ -1,15 +1,15 @@
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Skeleton({ function Skeleton({
className, className,
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) { }: React.HTMLAttributes<HTMLDivElement>) {
return ( return (
<div <div
className={cn("animate-pulse rounded-md bg-primary/10", className)} className={cn("animate-pulse rounded-md bg-primary/10", className)}
{...props} {...props}
/> />
) );
} }
export { Skeleton } export { Skeleton };

View File

@@ -1,28 +1,28 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SliderPrimitive from "@radix-ui/react-slider" import * as SliderPrimitive from "@radix-ui/react-slider";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Slider = React.forwardRef< const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>, React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SliderPrimitive.Root <SliderPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex w-full touch-none select-none items-center", "relative flex w-full touch-none select-none items-center",
className className,
)} )}
{...props} {...props}
> >
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20"> <SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
<SliderPrimitive.Range className="absolute h-full bg-primary" /> <SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track> </SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" /> <SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root> </SliderPrimitive.Root>
)) ));
Slider.displayName = SliderPrimitive.Root.displayName Slider.displayName = SliderPrimitive.Root.displayName;
export { Slider } export { Slider };

View File

@@ -1,31 +1,31 @@
"use client" "use client";
import { useTheme } from "next-themes" import { useTheme } from "next-themes";
import { Toaster as Sonner } from "sonner" import { Toaster as Sonner } from "sonner";
type ToasterProps = React.ComponentProps<typeof Sonner> type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => { const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme() const { theme = "system" } = useTheme();
return ( return (
<Sonner <Sonner
theme={theme as ToasterProps["theme"]} theme={theme as ToasterProps["theme"]}
className="toaster group" className="toaster group"
toastOptions={{ toastOptions={{
classNames: { classNames: {
toast: toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg", "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground", description: "group-[.toast]:text-muted-foreground",
actionButton: actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground", "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton: cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground", "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
}, },
}} }}
{...props} {...props}
/> />
) );
} };
export { Toaster } export { Toaster };

View File

@@ -1,29 +1,29 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch" import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Switch = React.forwardRef< const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>, React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SwitchPrimitives.Root <SwitchPrimitives.Root
className={cn( className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
> >
<SwitchPrimitives.Thumb <SwitchPrimitives.Thumb
className={cn( className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0" "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
)} )}
/> />
</SwitchPrimitives.Root> </SwitchPrimitives.Root>
)) ));
Switch.displayName = SwitchPrimitives.Root.displayName Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch } export { Switch };

View File

@@ -1,120 +1,120 @@
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Table = React.forwardRef< const Table = React.forwardRef<
HTMLTableElement, HTMLTableElement,
React.HTMLAttributes<HTMLTableElement> React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto"> <div className="relative w-full overflow-auto">
<table <table
ref={ref} ref={ref}
className={cn("w-full caption-bottom text-sm", className)} className={cn("w-full caption-bottom text-sm", className)}
{...props} {...props}
/> />
</div> </div>
)) ));
Table.displayName = "Table" Table.displayName = "Table";
const TableHeader = React.forwardRef< const TableHeader = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement> React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
)) ));
TableHeader.displayName = "TableHeader" TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef< const TableBody = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement> React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tbody <tbody
ref={ref} ref={ref}
className={cn("[&_tr:last-child]:border-0", className)} className={cn("[&_tr:last-child]:border-0", className)}
{...props} {...props}
/> />
)) ));
TableBody.displayName = "TableBody" TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef< const TableFooter = React.forwardRef<
HTMLTableSectionElement, HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement> React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tfoot <tfoot
ref={ref} ref={ref}
className={cn( className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TableFooter.displayName = "TableFooter" TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef< const TableRow = React.forwardRef<
HTMLTableRowElement, HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement> React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<tr <tr
ref={ref} ref={ref}
className={cn( className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TableRow.displayName = "TableRow" TableRow.displayName = "TableRow";
const TableHead = React.forwardRef< const TableHead = React.forwardRef<
HTMLTableCellElement, HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement> React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<th <th
ref={ref} ref={ref}
className={cn( className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", "h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TableHead.displayName = "TableHead" TableHead.displayName = "TableHead";
const TableCell = React.forwardRef< const TableCell = React.forwardRef<
HTMLTableCellElement, HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement> React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<td <td
ref={ref} ref={ref}
className={cn( className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TableCell.displayName = "TableCell" TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef< const TableCaption = React.forwardRef<
HTMLTableCaptionElement, HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement> React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<caption <caption
ref={ref} ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)} className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
)) ));
TableCaption.displayName = "TableCaption" TableCaption.displayName = "TableCaption";
export { export {
Table, Table,
TableHeader, TableHeader,
TableBody, TableBody,
TableFooter, TableFooter,
TableHead, TableHead,
TableRow, TableRow,
TableCell, TableCell,
TableCaption, TableCaption,
} };

View File

@@ -1,55 +1,55 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs" import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const Tabs = TabsPrimitive.Root const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef< const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>, React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<TabsPrimitive.List <TabsPrimitive.List
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TabsList.displayName = TabsPrimitive.List.displayName TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef< const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>, React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm", "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef< const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>, React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<TabsPrimitive.Content <TabsPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TabsContent.displayName = TabsPrimitive.Content.displayName TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent } export { Tabs, TabsList, TabsTrigger, TabsContent };

View File

@@ -5,142 +5,142 @@ import { motion } from "framer-motion";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
type Tab = { type Tab = {
title: string; title: string;
value: string; value: string;
content?: string | React.ReactNode | any; content?: string | React.ReactNode | any;
}; };
export const Tabs = ({ export const Tabs = ({
tabs: propTabs, tabs: propTabs,
containerClassName, containerClassName,
activeTabClassName, activeTabClassName,
tabClassName, tabClassName,
contentClassName, contentClassName,
}: { }: {
tabs: Tab[]; tabs: Tab[];
containerClassName?: string; containerClassName?: string;
activeTabClassName?: string; activeTabClassName?: string;
tabClassName?: string; tabClassName?: string;
contentClassName?: string; contentClassName?: string;
}) => { }) => {
const [active, setActive] = useState<Tab>(propTabs[0]); const [active, setActive] = useState<Tab>(propTabs[0]);
const [tabs, setTabs] = useState<Tab[]>(propTabs); const [tabs, setTabs] = useState<Tab[]>(propTabs);
const moveSelectedTabToTop = (idx: number) => { const moveSelectedTabToTop = (idx: number) => {
const newTabs = [...propTabs]; const newTabs = [...propTabs];
const selectedTab = newTabs.splice(idx, 1); const selectedTab = newTabs.splice(idx, 1);
newTabs.unshift(selectedTab[0]); newTabs.unshift(selectedTab[0]);
setTabs(newTabs); setTabs(newTabs);
setActive(newTabs[0]); setActive(newTabs[0]);
}; };
const [hovering, setHovering] = useState(false); const [hovering, setHovering] = useState(false);
return ( return (
<> <>
<div <div
className={cn( className={cn(
"flex flex-row items-center justify-start mt-0 [perspective:1000px] relative overflow-auto sm:overflow-visible no-visible-scrollbar border-x w-full border-t max-w-max bg-opacity-0", "flex flex-row items-center justify-start mt-0 [perspective:1000px] relative overflow-auto sm:overflow-visible no-visible-scrollbar border-x w-full border-t max-w-max bg-opacity-0",
containerClassName containerClassName,
)} )}
> >
{propTabs.map((tab, idx) => ( {propTabs.map((tab, idx) => (
<button <button
key={tab.title} key={tab.title}
onClick={() => { onClick={() => {
moveSelectedTabToTop(idx); moveSelectedTabToTop(idx);
}} }}
onMouseEnter={() => setHovering(true)} onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)} onMouseLeave={() => setHovering(false)}
className={cn( className={cn(
"relative px-4 py-2 rounded-full opacity-80 hover:opacity-100", "relative px-4 py-2 rounded-full opacity-80 hover:opacity-100",
tabClassName tabClassName,
)} )}
style={{ style={{
transformStyle: "preserve-3d", transformStyle: "preserve-3d",
}} }}
> >
{active.value === tab.value && ( {active.value === tab.value && (
<motion.div <motion.div
transition={{ transition={{
duration: 0.2, duration: 0.2,
delay: 0.1, delay: 0.1,
type: "keyframes", type: "keyframes",
}} }}
animate={{ animate={{
x: tabs.indexOf(tab) === 0 ? [0, 0, 0] : [0, 0, 0], x: tabs.indexOf(tab) === 0 ? [0, 0, 0] : [0, 0, 0],
}} }}
className={cn( className={cn(
"absolute inset-0 bg-gray-200 dark:bg-zinc-900/90 opacity-100", "absolute inset-0 bg-gray-200 dark:bg-zinc-900/90 opacity-100",
activeTabClassName activeTabClassName,
)} )}
/> />
)} )}
<span <span
className={cn( className={cn(
"relative block text-black dark:text-white", "relative block text-black dark:text-white",
active.value === tab.value active.value === tab.value
? "text-opacity-100 font-medium" ? "text-opacity-100 font-medium"
: "opacity-40 " : "opacity-40 ",
)} )}
> >
{tab.title} {tab.title}
</span> </span>
</button> </button>
))} ))}
</div> </div>
<FadeInDiv <FadeInDiv
tabs={tabs} tabs={tabs}
active={active} active={active}
key={active.value} key={active.value}
hovering={hovering} hovering={hovering}
className={cn("", contentClassName)} className={cn("", contentClassName)}
/> />
</> </>
); );
}; };
export const FadeInDiv = ({ export const FadeInDiv = ({
className, className,
tabs, tabs,
}: { }: {
className?: string; className?: string;
key?: string; key?: string;
tabs: Tab[]; tabs: Tab[];
active: Tab; active: Tab;
hovering?: boolean; hovering?: boolean;
}) => { }) => {
const isActive = (tab: Tab) => { const isActive = (tab: Tab) => {
return tab.value === tabs[0].value; return tab.value === tabs[0].value;
}; };
return ( return (
<div className="relative w-full h-full"> <div className="relative w-full h-full">
{tabs.map((tab, idx) => ( {tabs.map((tab, idx) => (
<motion.div <motion.div
key={tab.value} key={tab.value}
style={{ style={{
scale: 1 - idx * 0.1, scale: 1 - idx * 0.1,
zIndex: -idx, zIndex: -idx,
opacity: idx < 3 ? 1 - idx * 0.1 : 0, opacity: idx < 3 ? 1 - idx * 0.1 : 0,
}} }}
animate={{ animate={{
transition: { transition: {
duration: 0.2, duration: 0.2,
delay: 0.1, delay: 0.1,
type: "keyframes", type: "keyframes",
}, },
}} }}
className={cn( className={cn(
"w-50 h-full", "w-50 h-full",
isActive(tab) ? "" : "hidden", isActive(tab) ? "" : "hidden",
className className,
)} )}
> >
{tab.content} {tab.content}
</motion.div> </motion.div>
))} ))}
</div> </div>
); );
}; };

View File

@@ -1,24 +1,24 @@
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
export interface TextareaProps export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
return ( return (
<textarea <textarea
className={cn( className={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
) );
} },
) );
Textarea.displayName = "Textarea" Textarea.displayName = "Textarea";
export { Textarea } export { Textarea };

View File

@@ -1,129 +1,129 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { Cross2Icon } from "@radix-ui/react-icons" import { Cross2Icon } from "@radix-ui/react-icons";
import * as ToastPrimitives from "@radix-ui/react-toast" import * as ToastPrimitives from "@radix-ui/react-toast";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const ToastProvider = ToastPrimitives.Provider const ToastProvider = ToastPrimitives.Provider;
const ToastViewport = React.forwardRef< const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>, React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport <ToastPrimitives.Viewport
ref={ref} ref={ref}
className={cn( className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className className,
)} )}
{...props} {...props}
/> />
)) ));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
const toastVariants = cva( const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{ {
variants: { variants: {
variant: { variant: {
default: "border bg-background text-foreground", default: "border bg-background text-foreground",
destructive: destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground", "destructive group border-destructive bg-destructive text-destructive-foreground",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
const Toast = React.forwardRef< const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>, React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants> VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => { >(({ className, variant, ...props }, ref) => {
return ( return (
<ToastPrimitives.Root <ToastPrimitives.Root
ref={ref} ref={ref}
className={cn(toastVariants({ variant }), className)} className={cn(toastVariants({ variant }), className)}
{...props} {...props}
/> />
) );
}) });
Toast.displayName = ToastPrimitives.Root.displayName Toast.displayName = ToastPrimitives.Root.displayName;
const ToastAction = React.forwardRef< const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>, React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ToastPrimitives.Action <ToastPrimitives.Action
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className className,
)} )}
{...props} {...props}
/> />
)) ));
ToastAction.displayName = ToastPrimitives.Action.displayName ToastAction.displayName = ToastPrimitives.Action.displayName;
const ToastClose = React.forwardRef< const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>, React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ToastPrimitives.Close <ToastPrimitives.Close
ref={ref} ref={ref}
className={cn( className={cn(
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", "absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className className,
)} )}
toast-close="" toast-close=""
{...props} {...props}
> >
<Cross2Icon className="h-4 w-4" /> <Cross2Icon className="h-4 w-4" />
</ToastPrimitives.Close> </ToastPrimitives.Close>
)) ));
ToastClose.displayName = ToastPrimitives.Close.displayName ToastClose.displayName = ToastPrimitives.Close.displayName;
const ToastTitle = React.forwardRef< const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>, React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ToastPrimitives.Title <ToastPrimitives.Title
ref={ref} ref={ref}
className={cn("text-sm font-semibold [&+div]:text-xs", className)} className={cn("text-sm font-semibold [&+div]:text-xs", className)}
{...props} {...props}
/> />
)) ));
ToastTitle.displayName = ToastPrimitives.Title.displayName ToastTitle.displayName = ToastPrimitives.Title.displayName;
const ToastDescription = React.forwardRef< const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>, React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ToastPrimitives.Description <ToastPrimitives.Description
ref={ref} ref={ref}
className={cn("text-sm opacity-90", className)} className={cn("text-sm opacity-90", className)}
{...props} {...props}
/> />
)) ));
ToastDescription.displayName = ToastPrimitives.Description.displayName ToastDescription.displayName = ToastPrimitives.Description.displayName;
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
type ToastActionElement = React.ReactElement<typeof ToastAction> type ToastActionElement = React.ReactElement<typeof ToastAction>;
export { export {
type ToastProps, type ToastProps,
type ToastActionElement, type ToastActionElement,
ToastProvider, ToastProvider,
ToastViewport, ToastViewport,
Toast, Toast,
ToastTitle, ToastTitle,
ToastDescription, ToastDescription,
ToastClose, ToastClose,
ToastAction, ToastAction,
} };

View File

@@ -1,35 +1,35 @@
"use client" "use client";
import { useToast } from "@/hooks/use-toast" import { useToast } from "@/hooks/use-toast";
import { import {
Toast, Toast,
ToastClose, ToastClose,
ToastDescription, ToastDescription,
ToastProvider, ToastProvider,
ToastTitle, ToastTitle,
ToastViewport, ToastViewport,
} from "@/components/ui/toast" } from "@/components/ui/toast";
export function Toaster() { export function Toaster() {
const { toasts } = useToast() const { toasts } = useToast();
return ( return (
<ToastProvider> <ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) { {toasts.map(function ({ id, title, description, action, ...props }) {
return ( return (
<Toast key={id} {...props}> <Toast key={id} {...props}>
<div className="grid gap-1"> <div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>} {title && <ToastTitle>{title}</ToastTitle>}
{description && ( {description && (
<ToastDescription>{description}</ToastDescription> <ToastDescription>{description}</ToastDescription>
)} )}
</div> </div>
{action} {action}
<ToastClose /> <ToastClose />
</Toast> </Toast>
) );
})} })}
<ToastViewport /> <ToastViewport />
</ToastProvider> </ToastProvider>
) );
} }

View File

@@ -1,61 +1,61 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import { type VariantProps } from "class-variance-authority" import { type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { toggleVariants } from "@/components/ui/toggle" import { toggleVariants } from "@/components/ui/toggle";
const ToggleGroupContext = React.createContext< const ToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>({ >({
size: "default", size: "default",
variant: "default", variant: "default",
}) });
const ToggleGroup = React.forwardRef< const ToggleGroup = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Root>, React.ElementRef<typeof ToggleGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> & React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, variant, size, children, ...props }, ref) => ( >(({ className, variant, size, children, ...props }, ref) => (
<ToggleGroupPrimitive.Root <ToggleGroupPrimitive.Root
ref={ref} ref={ref}
className={cn("flex items-center justify-center gap-1", className)} className={cn("flex items-center justify-center gap-1", className)}
{...props} {...props}
> >
<ToggleGroupContext.Provider value={{ variant, size }}> <ToggleGroupContext.Provider value={{ variant, size }}>
{children} {children}
</ToggleGroupContext.Provider> </ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root> </ToggleGroupPrimitive.Root>
)) ));
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
const ToggleGroupItem = React.forwardRef< const ToggleGroupItem = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Item>, React.ElementRef<typeof ToggleGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, children, variant, size, ...props }, ref) => { >(({ className, children, variant, size, ...props }, ref) => {
const context = React.useContext(ToggleGroupContext) const context = React.useContext(ToggleGroupContext);
return ( return (
<ToggleGroupPrimitive.Item <ToggleGroupPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
toggleVariants({ toggleVariants({
variant: context.variant || variant, variant: context.variant || variant,
size: context.size || size, size: context.size || size,
}), }),
className className,
)} )}
{...props} {...props}
> >
{children} {children}
</ToggleGroupPrimitive.Item> </ToggleGroupPrimitive.Item>
) );
}) });
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
export { ToggleGroup, ToggleGroupItem } export { ToggleGroup, ToggleGroupItem };

View File

@@ -1,45 +1,45 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as TogglePrimitive from "@radix-ui/react-toggle" import * as TogglePrimitive from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const toggleVariants = cva( const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground", "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-transparent", default: "bg-transparent",
outline: outline:
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground", "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
}, },
size: { size: {
default: "h-9 px-3", default: "h-9 px-3",
sm: "h-8 px-2", sm: "h-8 px-2",
lg: "h-10 px-3", lg: "h-10 px-3",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
) );
const Toggle = React.forwardRef< const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>, React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => ( >(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root <TogglePrimitive.Root
ref={ref} ref={ref}
className={cn(toggleVariants({ variant, size, className }))} className={cn(toggleVariants({ variant, size, className }))}
{...props} {...props}
/> />
)) ));
Toggle.displayName = TogglePrimitive.Root.displayName Toggle.displayName = TogglePrimitive.Root.displayName;
export { Toggle, toggleVariants } export { Toggle, toggleVariants };

View File

@@ -1,30 +1,30 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip" import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const TooltipProvider = TooltipPrimitive.Provider const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef< const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>, React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => ( >(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content <TooltipPrimitive.Content
ref={ref} ref={ref}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className className,
)} )}
{...props} {...props}
/> />
)) ));
TooltipContent.displayName = TooltipPrimitive.Content.displayName TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

View File

@@ -1,42 +1,36 @@
"use client" "use client";
import Link from "next/link"; import Link from "next/link";
import { ThemeToggle } from "./theme-toggle"; import { ThemeToggle } from "./theme-toggle";
import { Logo } from "./logo"; import { Logo } from "./logo";
import { import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
export function Wrapper(props: { children: React.ReactNode }) { export function Wrapper(props: { children: React.ReactNode }) {
return ( return (
<div className="min-h-screen w-full dark:bg-black bg-white dark:bg-grid-small-white/[0.2] bg-grid-small-black/[0.2] relative flex justify-center"> <div className="min-h-screen w-full dark:bg-black bg-white dark:bg-grid-small-white/[0.2] bg-grid-small-black/[0.2] relative flex justify-center">
<div className="absolute pointer-events-none inset-0 md:flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)] hidden"></div> <div className="absolute pointer-events-none inset-0 md:flex items-center justify-center dark:bg-black bg-white [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)] hidden"></div>
<div className="bg-white dark:bg-black border-b py-2 flex justify-between items-center border-border absolute z-50 w-full lg:w-8/12 px-4 md:px-1"> <div className="bg-white dark:bg-black border-b py-2 flex justify-between items-center border-border absolute z-50 w-full lg:w-8/12 px-4 md:px-1">
<Link href="/"> <Link href="/">
<div className="flex gap-2 cursor-pointer"> <div className="flex gap-2 cursor-pointer">
<Logo /> <Logo />
<p className="dark:text-white text-black">BETTER-AUTH.</p> <p className="dark:text-white text-black">BETTER-AUTH.</p>
</div> </div>
</Link> </Link>
<div className="z-50 flex items-center"> <div className="z-50 flex items-center">
<ThemeToggle /> <ThemeToggle />
</div> </div>
</div> </div>
<div className="mt-20 lg:w-7/12 w-full"> <div className="mt-20 lg:w-7/12 w-full">{props.children}</div>
{props.children} </div>
</div> );
</div>
)
} }
const queryClient = new QueryClient();
const queryClient = new QueryClient()
export function WrapperWithQuery(props: { children: React.ReactNode }) { export function WrapperWithQuery(props: { children: React.ReactNode }) {
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
{props.children} {props.children}
</QueryClientProvider> </QueryClientProvider>
) );
} }

View File

@@ -95,6 +95,7 @@ export const reducer = (state: State, action: Action): State => {
if (toastId) { if (toastId) {
addToRemoveQueue(toastId); addToRemoveQueue(toastId);
} else { } else {
// biome-ignore lint/complexity/noForEach: <explanation>
state.toasts.forEach((toast) => { state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id); addToRemoveQueue(toast.id);
}); });
@@ -132,6 +133,7 @@ let memoryState: State = { toasts: [] };
function dispatch(action: Action) { function dispatch(action: Action) {
memoryState = reducer(memoryState, action); memoryState = reducer(memoryState, action);
// biome-ignore lint/complexity/noForEach: <explanation>
listeners.forEach((listener) => { listeners.forEach((listener) => {
listener(memoryState); listener(memoryState);
}); });

View File

@@ -1,110 +1,108 @@
import { import {
Body, Body,
Button, Button,
Container, Container,
Column, Column,
Head, Head,
Heading, Heading,
Hr, Hr,
Html, Html,
Img, Img,
Link, Link,
Preview, Preview,
Row, Row,
Section, Section,
Text, Text,
Tailwind, Tailwind,
} from "@react-email/components"; } from "@react-email/components";
import * as React from "react"; import * as React from "react";
interface BetterAuthInviteUserEmailProps { interface BetterAuthInviteUserEmailProps {
username?: string; username?: string;
invitedByUsername?: string; invitedByUsername?: string;
invitedByEmail?: string; invitedByEmail?: string;
teamName?: string; teamName?: string;
teamImage?: string; teamImage?: string;
inviteLink?: string; inviteLink?: string;
} }
export const InviteUserEmail = ({ export const InviteUserEmail = ({
username, username,
invitedByUsername, invitedByUsername,
invitedByEmail, invitedByEmail,
teamName, teamName,
teamImage, teamImage,
inviteLink, inviteLink,
}: BetterAuthInviteUserEmailProps) => { }: BetterAuthInviteUserEmailProps) => {
const previewText = `Join ${invitedByUsername} on BetterAuth`; const previewText = `Join ${invitedByUsername} on BetterAuth`;
return ( return (
<Html> <Html>
<Head /> <Head />
<Preview>{previewText}</Preview> <Preview>{previewText}</Preview>
<Tailwind> <Tailwind>
<Body className="bg-white my-auto mx-auto font-sans px-2"> <Body className="bg-white my-auto mx-auto font-sans px-2">
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]"> <Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0"> <Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
Join <strong>{invitedByUsername}</strong> on <strong>Better Auth.</strong> Join <strong>{invitedByUsername}</strong> on{" "}
</Heading> <strong>Better Auth.</strong>
<Text className="text-black text-[14px] leading-[24px]"> </Heading>
Hello there, <Text className="text-black text-[14px] leading-[24px]">
</Text> Hello there,
<Text className="text-black text-[14px] leading-[24px]"> </Text>
<strong>{invitedByUsername}</strong> ( <Text className="text-black text-[14px] leading-[24px]">
<Link <strong>{invitedByUsername}</strong> (
href={`mailto:${invitedByEmail}`} <Link
className="text-blue-600 no-underline" href={`mailto:${invitedByEmail}`}
> className="text-blue-600 no-underline"
{invitedByEmail} >
</Link> {invitedByEmail}
) has invited you to the <strong>{teamName}</strong> team on{" "} </Link>
<strong>Better Auth</strong>. ) has invited you to the <strong>{teamName}</strong> team on{" "}
</Text> <strong>Better Auth</strong>.
<Section> </Text>
{ <Section>
teamImage ? <Row> {teamImage ? (
<Column align="left"> <Row>
<Img <Column align="left">
className="rounded-full" <Img
src={teamImage} className="rounded-full"
width="64" src={teamImage}
height="64" width="64"
fetchPriority="high" height="64"
/> fetchPriority="high"
</Column> />
</Row> : null </Column>
} </Row>
</Section> ) : null}
<Section className="text-center mt-[32px] mb-[32px]"> </Section>
<Button <Section className="text-center mt-[32px] mb-[32px]">
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3" <Button
href={inviteLink} className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
> href={inviteLink}
Join the team >
</Button> Join the team
</Section> </Button>
<Text className="text-black text-[14px] leading-[24px]"> </Section>
or copy and paste this URL into your browser:{" "} <Text className="text-black text-[14px] leading-[24px]">
<Link href={inviteLink} className="text-blue-600 no-underline"> or copy and paste this URL into your browser:{" "}
{inviteLink} <Link href={inviteLink} className="text-blue-600 no-underline">
</Link> {inviteLink}
</Text> </Link>
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" /> </Text>
<Text className="text-[#666666] text-[12px] leading-[24px]"> <Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
This invitation was intended for{" "} <Text className="text-[#666666] text-[12px] leading-[24px]">
<span className="text-black">{username}</span>. If you This invitation was intended for{" "}
were not expecting this invitation, you can ignore this email. <span className="text-black">{username}</span>. If you were not
</Text> expecting this invitation, you can ignore this email.
</Container> </Text>
</Body> </Container>
</Tailwind> </Body>
</Html> </Tailwind>
); </Html>
);
}; };
export function reactInvitationEmail(props: BetterAuthInviteUserEmailProps) { export function reactInvitationEmail(props: BetterAuthInviteUserEmailProps) {
console.log(props) console.log(props);
return <InviteUserEmail {...props} /> return <InviteUserEmail {...props} />;
} }

View File

@@ -1,71 +1,76 @@
import { import {
Body, Body,
Button, Button,
Container, Container,
Head, Head,
Heading, Heading,
Hr, Hr,
Html, Html,
Link, Link,
Preview, Preview,
Text, Text,
Tailwind, Tailwind,
Section, Section,
} from "@react-email/components"; } from "@react-email/components";
import * as React from "react"; import * as React from "react";
interface BetterAuthResetPasswordEmailProps { interface BetterAuthResetPasswordEmailProps {
username?: string; username?: string;
resetLink?: string; resetLink?: string;
} }
export const ResetPasswordEmail = ({ export const ResetPasswordEmail = ({
username, username,
resetLink, resetLink,
}: BetterAuthResetPasswordEmailProps) => { }: BetterAuthResetPasswordEmailProps) => {
const previewText = `Reset your BetterAuth password`; const previewText = `Reset your BetterAuth password`;
return ( return (
<Html> <Html>
<Head /> <Head />
<Preview>{previewText}</Preview> <Preview>{previewText}</Preview>
<Tailwind> <Tailwind>
<Body className="bg-white my-auto mx-auto font-sans px-2"> <Body className="bg-white my-auto mx-auto font-sans px-2">
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]"> <Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0"> <Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
Reset your <strong>Better Auth</strong> password Reset your <strong>Better Auth</strong> password
</Heading> </Heading>
<Text className="text-black text-[14px] leading-[24px]"> <Text className="text-black text-[14px] leading-[24px]">
Hello {username}, Hello {username},
</Text> </Text>
<Text className="text-black text-[14px] leading-[24px]"> <Text className="text-black text-[14px] leading-[24px]">
We received a request to reset your password for your Better Auth account. If you didn't make this request, you can safely ignore this email. We received a request to reset your password for your Better Auth
</Text> account. If you didn't make this request, you can safely ignore
<Section className="text-center mt-[32px] mb-[32px]"> this email.
<Button </Text>
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3" <Section className="text-center mt-[32px] mb-[32px]">
href={resetLink} <Button
> className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
Reset Password href={resetLink}
</Button> >
</Section> Reset Password
<Text className="text-black text-[14px] leading-[24px]"> </Button>
Or copy and paste this URL into your browser:{" "} </Section>
<Link href={resetLink} className="text-blue-600 no-underline"> <Text className="text-black text-[14px] leading-[24px]">
{resetLink} Or copy and paste this URL into your browser:{" "}
</Link> <Link href={resetLink} className="text-blue-600 no-underline">
</Text> {resetLink}
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" /> </Link>
<Text className="text-[#666666] text-[12px] leading-[24px]"> </Text>
If you didn't request a password reset, please ignore this email or contact support if you have concerns. <Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
</Text> <Text className="text-[#666666] text-[12px] leading-[24px]">
</Container> If you didn't request a password reset, please ignore this email
</Body> or contact support if you have concerns.
</Tailwind> </Text>
</Html> </Container>
); </Body>
</Tailwind>
</Html>
);
}; };
export function reactResetPasswordEmail(props: BetterAuthResetPasswordEmailProps) { export function reactResetPasswordEmail(
console.log(props); props: BetterAuthResetPasswordEmailProps,
return <ResetPasswordEmail {...props} />; ) {
console.log(props);
return <ResetPasswordEmail {...props} />;
} }

View File

@@ -1,6 +1,6 @@
import { clsx, type ClassValue } from "clsx" import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs));
} }

View File

@@ -1,8 +1,8 @@
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
}, },
}; };
export default config; export default config;

View File

@@ -4,126 +4,126 @@ const svgToDataUri = require("mini-svg-data-uri");
const colors = require("tailwindcss/colors"); const colors = require("tailwindcss/colors");
const { const {
default: flattenColorPalette, default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette"); } = require("tailwindcss/lib/util/flattenColorPalette");
const config = { const config = {
darkMode: ["class"], darkMode: ["class"],
content: [ content: [
"./pages/**/*.{ts,tsx}", "./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}",
], ],
prefix: "", prefix: "",
theme: { theme: {
container: { container: {
center: true, center: true,
padding: "2rem", padding: "2rem",
screens: { screens: {
"2xl": "1400px", "2xl": "1400px",
}, },
}, },
extend: { extend: {
colors: { colors: {
border: "hsl(var(--border))", border: "hsl(var(--border))",
input: "hsl(var(--input))", input: "hsl(var(--input))",
ring: "hsl(var(--ring))", ring: "hsl(var(--ring))",
background: "hsl(var(--background))", background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))", foreground: "hsl(var(--foreground))",
primary: { primary: {
DEFAULT: "hsl(var(--primary))", DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))", foreground: "hsl(var(--primary-foreground))",
}, },
secondary: { secondary: {
DEFAULT: "hsl(var(--secondary))", DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))", foreground: "hsl(var(--secondary-foreground))",
}, },
destructive: { destructive: {
DEFAULT: "hsl(var(--destructive))", DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))", foreground: "hsl(var(--destructive-foreground))",
}, },
muted: { muted: {
DEFAULT: "hsl(var(--muted))", DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))", foreground: "hsl(var(--muted-foreground))",
}, },
accent: { accent: {
DEFAULT: "hsl(var(--accent))", DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))", foreground: "hsl(var(--accent-foreground))",
}, },
popover: { popover: {
DEFAULT: "hsl(var(--popover))", DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))", foreground: "hsl(var(--popover-foreground))",
}, },
card: { card: {
DEFAULT: "hsl(var(--card))", DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))", foreground: "hsl(var(--card-foreground))",
}, },
}, },
borderRadius: { borderRadius: {
lg: "var(--radius)", lg: "var(--radius)",
md: "calc(var(--radius) - 2px)", md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)", sm: "calc(var(--radius) - 4px)",
}, },
keyframes: { keyframes: {
"accordion-down": { "accordion-down": {
from: { height: "0" }, from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" }, to: { height: "var(--radix-accordion-content-height)" },
}, },
"accordion-up": { "accordion-up": {
from: { height: "var(--radix-accordion-content-height)" }, from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" }, to: { height: "0" },
}, },
}, },
animation: { animation: {
"accordion-down": "accordion-down 0.2s ease-out", "accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out",
}, },
boxShadow: { boxShadow: {
input: `0px 2px 3px -1px rgba(0,0,0,0.1), 0px 1px 0px 0px rgba(25,28,33,0.02), 0px 0px 0px 1px rgba(25,28,33,0.08)`, input: `0px 2px 3px -1px rgba(0,0,0,0.1), 0px 1px 0px 0px rgba(25,28,33,0.02), 0px 0px 0px 1px rgba(25,28,33,0.08)`,
}, },
}, },
}, },
plugins: [ plugins: [
require("tailwindcss-animate"), require("tailwindcss-animate"),
addVariablesForColors, addVariablesForColors,
function ({ matchUtilities, theme }: any) { function ({ matchUtilities, theme }: any) {
matchUtilities( matchUtilities(
{ {
"bg-grid": (value: any) => ({ "bg-grid": (value: any) => ({
backgroundImage: `url("${svgToDataUri( backgroundImage: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>` `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
)}")`, )}")`,
}), }),
"bg-grid-small": (value: any) => ({ "bg-grid-small": (value: any) => ({
backgroundImage: `url("${svgToDataUri( backgroundImage: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="8" height="8" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>` `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="8" height="8" fill="none" stroke="${value}"><path d="M0 .5H31.5V32"/></svg>`,
)}")`, )}")`,
}), }),
"bg-dot": (value: any) => ({ "bg-dot": (value: any) => ({
backgroundImage: `url("${svgToDataUri( backgroundImage: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="1.6257413380501518"></circle></svg>` `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="1.6257413380501518"></circle></svg>`,
)}")`, )}")`,
}), }),
}, },
{ {
values: flattenColorPalette(theme("backgroundColor")), values: flattenColorPalette(theme("backgroundColor")),
type: "color", type: "color",
} },
); );
}, },
], ],
} satisfies Config; } satisfies Config;
function addVariablesForColors({ addBase, theme }: any) { function addVariablesForColors({ addBase, theme }: any) {
let allColors = flattenColorPalette(theme("colors")); let allColors = flattenColorPalette(theme("colors"));
let newVars = Object.fromEntries( let newVars = Object.fromEntries(
Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) Object.entries(allColors).map(([key, val]) => [`--${key}`, val]),
); );
addBase({ addBase({
":root": newVars, ":root": newVars,
}); });
} }
export default config; export default config;

View File

@@ -1,27 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
"name": "next" "name": "next"
} }
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

View File

@@ -1,27 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
// Enable latest features // Enable latest features
"lib": ["ESNext", "DOM"], "lib": ["ESNext", "DOM"],
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleDetection": "force", "moduleDetection": "force",
"jsx": "react-jsx", "jsx": "react-jsx",
"allowJs": true, "allowJs": true,
// Bundler mode // Bundler mode
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"noEmit": true, "noEmit": true,
// Best practices // Best practices
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default) // Some stricter flags (disabled by default)
"noUnusedLocals": false, "noUnusedLocals": false,
"noUnusedParameters": false, "noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false "noPropertyAccessFromIndexSignature": false
} }
} }

View File

@@ -11,16 +11,18 @@ export default function Home() {
return ( return (
<main class="text-center mx-auto text-gray-700 p-4 min-h-screen flex flex-col items-center justify-center"> <main class="text-center mx-auto text-gray-700 p-4 min-h-screen flex flex-col items-center justify-center">
<Show <Show
fallback={<button fallback={
class="bg-sky-600 text-white px-4 py-2 rounded-md" <button
onclick={async () => { class="bg-sky-600 text-white px-4 py-2 rounded-md"
await client.signIn.social({ onclick={async () => {
provider: "github", await client.signIn.social({
}); provider: "github",
}} });
> }}
Continue with github >
</button>} Continue with github
</button>
}
when={!!session().data?.session} when={!!session().data?.session}
> >
<div class="flex flex-col gap-2 border border-blue-400/10 px-6 py-5"> <div class="flex flex-col gap-2 border border-blue-400/10 px-6 py-5">

View File

@@ -5,5 +5,3 @@ export const client = createAuthClient({
baseURL: "http://localhost:3000/api/auth", baseURL: "http://localhost:3000/api/auth",
plugins: [organizationClient()], plugins: [organizationClient()],
}); });

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { client } from "$lib/client"; import { client } from "$lib/client";
const organizations = client.useListOrganizations(); const organizations = client.useListOrganizations();
const activeOrganization = client.useActiveOrganization(); const activeOrganization = client.useActiveOrganization();
</script> </script>
<h1>Organizations</h1> <h1>Organizations</h1>

View File

@@ -1,111 +1,110 @@
import { useId } from 'react' import { useId } from "react";
import { Intro, IntroFooter } from "./changelog-layout" import { Intro, IntroFooter } from "./changelog-layout";
import { StarField } from './stat-field' import { StarField } from "./stat-field";
function Timeline() { function Timeline() {
let id = useId() let id = useId();
return ( return (
<div className="pointer-events-none absolute inset-0 z-50 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem] lg:overflow-visible"> <div className="pointer-events-none absolute inset-0 z-50 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem] lg:overflow-visible">
<svg <svg
className="absolute left-[max(0px,calc(50%-18.125rem))] top-0 h-full w-1.5 lg:left-full lg:ml-1 xl:left-auto xl:right-1 xl:ml-0" className="absolute left-[max(0px,calc(50%-18.125rem))] top-0 h-full w-1.5 lg:left-full lg:ml-1 xl:left-auto xl:right-1 xl:ml-0"
aria-hidden="true" aria-hidden="true"
> >
<defs> <defs>
<pattern id={id} width="6" height="8" patternUnits="userSpaceOnUse"> <pattern id={id} width="6" height="8" patternUnits="userSpaceOnUse">
<path <path
d="M0 0H6M0 8H6" d="M0 0H6M0 8H6"
className="stroke-sky-900/10 xl:stroke-white/10 dark:stroke-white/10" className="stroke-sky-900/10 xl:stroke-white/10 dark:stroke-white/10"
fill="none" fill="none"
/> />
</pattern> </pattern>
</defs> </defs>
<rect width="100%" height="100%" fill={`url(#${id})`} /> <rect width="100%" height="100%" fill={`url(#${id})`} />
</svg> </svg>
someone is someone is
</div> </div>
) );
} }
function Glow() { function Glow() {
let id = useId() let id = useId();
return ( return (
<div className="absolute inset-0 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem]"> <div className="absolute inset-0 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem]">
<svg <svg
className="absolute -bottom-48 left-[-40%] h-[80rem] w-[180%] lg:-right-40 lg:bottom-auto lg:left-auto lg:top-[-40%] lg:h-[180%] lg:w-[80rem]" className="absolute -bottom-48 left-[-40%] h-[80rem] w-[180%] lg:-right-40 lg:bottom-auto lg:left-auto lg:top-[-40%] lg:h-[180%] lg:w-[80rem]"
aria-hidden="true" aria-hidden="true"
> >
<defs> <defs>
<radialGradient id={`${id}-desktop`} cx="100%"> <radialGradient id={`${id}-desktop`} cx="100%">
<stop offset="0%" stopColor="rgba(214, 211, 209, 0.6)" /> <stop offset="0%" stopColor="rgba(214, 211, 209, 0.6)" />
<stop offset="53.95%" stopColor="rgba(214, 200, 209, 0.09)" /> <stop offset="53.95%" stopColor="rgba(214, 200, 209, 0.09)" />
<stop offset="100%" stopColor="rgba(10, 14, 23, 0)" /> <stop offset="100%" stopColor="rgba(10, 14, 23, 0)" />
</radialGradient> </radialGradient>
<radialGradient id={`${id}-mobile`} cy="100%"> <radialGradient id={`${id}-mobile`} cy="100%">
<stop offset="0%" stopColor="rgba(56, 189, 248, 0.3)" /> <stop offset="0%" stopColor="rgba(56, 189, 248, 0.3)" />
<stop offset="53.95%" stopColor="rgba(0, 71, 255, 0.09)" /> <stop offset="53.95%" stopColor="rgba(0, 71, 255, 0.09)" />
<stop offset="100%" stopColor="rgba(10, 14, 23, 0)" /> <stop offset="100%" stopColor="rgba(10, 14, 23, 0)" />
</radialGradient> </radialGradient>
</defs> </defs>
<rect <rect
width="100%" width="100%"
height="100%" height="100%"
fill={`url(#${id}-desktop)`} fill={`url(#${id}-desktop)`}
className="hidden lg:block" className="hidden lg:block"
/> />
<rect <rect
width="100%" width="100%"
height="100%" height="100%"
fill={`url(#${id}-mobile)`} fill={`url(#${id}-mobile)`}
className="lg:hidden" className="lg:hidden"
/> />
</svg> </svg>
<div className="absolute inset-x-0 bottom-0 right-0 h-px bg-white mix-blend-overlay lg:left-auto lg:top-0 lg:h-auto lg:w-px" /> <div className="absolute inset-x-0 bottom-0 right-0 h-px bg-white mix-blend-overlay lg:left-auto lg:top-0 lg:h-auto lg:w-px" />
</div> </div>
) );
} }
function FixedSidebar({ function FixedSidebar({
main, main,
footer, footer,
}: { }: {
main: React.ReactNode main: React.ReactNode;
footer: React.ReactNode footer: React.ReactNode;
}) { }) {
return ( return (
<div className="relative flex-none overflow-hidden px-10 lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex lg:px-0"> <div className="relative flex-none overflow-hidden px-10 lg:pointer-events-none lg:fixed lg:inset-0 lg:z-40 lg:flex lg:px-0">
<Glow /> <Glow />
<div className="relative flex w-full lg:pointer-events-auto lg:mr-[calc(max(2rem,50%-35rem)+40rem)] lg:min-w-[32rem] lg:overflow-y-auto lg:overflow-x-hidden lg:pl-[max(4rem,calc(50%-38rem))]"> <div className="relative flex w-full lg:pointer-events-auto lg:mr-[calc(max(2rem,50%-35rem)+40rem)] lg:min-w-[32rem] lg:overflow-y-auto lg:overflow-x-hidden lg:pl-[max(4rem,calc(50%-38rem))]">
<div className="mx-auto max-w-lg lg:mx-auto lg:flex lg:max-w-4xl lg:flex-col lg:before:flex-1 lg:before:pt-6"> <div className="mx-auto max-w-lg lg:mx-auto lg:flex lg:max-w-4xl lg:flex-col lg:before:flex-1 lg:before:pt-6">
<div className="pb-16 pt-20 sm:pb-20 sm:pt-32 lg:py-20"> <div className="pb-16 pt-20 sm:pb-20 sm:pt-32 lg:py-20">
<div className="relative pr-10"> <div className="relative pr-10">
<StarField className="-right-44 top-14" /> <StarField className="-right-44 top-14" />
{main} {main}
</div> </div>
</div> </div>
<div className="flex flex-1 items-end justify-center pb-4 lg:justify-start lg:pb-6"> <div className="flex flex-1 items-end justify-center pb-4 lg:justify-start lg:pb-6">
{footer} {footer}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) );
} }
export function Layout({ children }: { children: React.ReactNode }) { export function Layout({ children }: { children: React.ReactNode }) {
return ( return (
<> <>
<FixedSidebar main={<Intro />} footer={<IntroFooter />} /> <FixedSidebar main={<Intro />} footer={<IntroFooter />} />
<div /> <div />
<div className="relative flex-auto"> <div className="relative flex-auto">
<Timeline /> <Timeline />
<main className="grid ml-auto grid-cols-12 col-span-5 ml-auto space-y-20 py-20 sm:space-y-32 sm:py-32"> <main className="grid ml-auto grid-cols-12 col-span-5 ml-auto space-y-20 py-20 sm:space-y-32 sm:py-32">
{children}
{children} </main>
</main> </div>
</div> </>
</> );
)
} }

View File

@@ -1,130 +1,140 @@
import Link from 'next/link' import Link from "next/link";
import { Button } from '@/components/ui/button' import { Button } from "@/components/ui/button";
import { useId } from 'react' import { useId } from "react";
import clsx from 'clsx' import clsx from "clsx";
function BookIcon(props: React.ComponentPropsWithoutRef<"svg">) {
function BookIcon(props: React.ComponentPropsWithoutRef<'svg'>) { return (
return ( <svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}>
<svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}> <path d="M7 3.41a1 1 0 0 0-.668-.943L2.275 1.039a.987.987 0 0 0-.877.166c-.25.192-.398.493-.398.812V12.2c0 .454.296.853.725.977l3.948 1.365A1 1 0 0 0 7 13.596V3.41ZM9 13.596a1 1 0 0 0 1.327.946l3.948-1.365c.429-.124.725-.523.725-.977V2.017c0-.32-.147-.62-.398-.812a.987.987 0 0 0-.877-.166L9.668 2.467A1 1 0 0 0 9 3.41v10.186Z" />
<path d="M7 3.41a1 1 0 0 0-.668-.943L2.275 1.039a.987.987 0 0 0-.877.166c-.25.192-.398.493-.398.812V12.2c0 .454.296.853.725.977l3.948 1.365A1 1 0 0 0 7 13.596V3.41ZM9 13.596a1 1 0 0 0 1.327.946l3.948-1.365c.429-.124.725-.523.725-.977V2.017c0-.32-.147-.62-.398-.812a.987.987 0 0 0-.877-.166L9.668 2.467A1 1 0 0 0 9 3.41v10.186Z" /> </svg>
</svg> );
)
} }
function GitHubIcon(props: React.ComponentPropsWithoutRef<'svg'>) { function GitHubIcon(props: React.ComponentPropsWithoutRef<"svg">) {
return ( return (
<svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}> <svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}>
<path d="M8 .198a8 8 0 0 0-8 8 7.999 7.999 0 0 0 5.47 7.59c.4.076.547-.172.547-.384 0-.19-.007-.694-.01-1.36-2.226.482-2.695-1.074-2.695-1.074-.364-.923-.89-1.17-.89-1.17-.725-.496.056-.486.056-.486.803.056 1.225.824 1.225.824.714 1.224 1.873.87 2.33.666.072-.518.278-.87.507-1.07-1.777-.2-3.644-.888-3.644-3.954 0-.873.31-1.586.823-2.146-.09-.202-.36-1.016.07-2.118 0 0 .67-.214 2.2.82a7.67 7.67 0 0 1 2-.27 7.67 7.67 0 0 1 2 .27c1.52-1.034 2.19-.82 2.19-.82.43 1.102.16 1.916.08 2.118.51.56.82 1.273.82 2.146 0 3.074-1.87 3.75-3.65 3.947.28.24.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.14.46.55.38A7.972 7.972 0 0 0 16 8.199a8 8 0 0 0-8-8Z" /> <path d="M8 .198a8 8 0 0 0-8 8 7.999 7.999 0 0 0 5.47 7.59c.4.076.547-.172.547-.384 0-.19-.007-.694-.01-1.36-2.226.482-2.695-1.074-2.695-1.074-.364-.923-.89-1.17-.89-1.17-.725-.496.056-.486.056-.486.803.056 1.225.824 1.225.824.714 1.224 1.873.87 2.33.666.072-.518.278-.87.507-1.07-1.777-.2-3.644-.888-3.644-3.954 0-.873.31-1.586.823-2.146-.09-.202-.36-1.016.07-2.118 0 0 .67-.214 2.2.82a7.67 7.67 0 0 1 2-.27 7.67 7.67 0 0 1 2 .27c1.52-1.034 2.19-.82 2.19-.82.43 1.102.16 1.916.08 2.118.51.56.82 1.273.82 2.146 0 3.074-1.87 3.75-3.65 3.947.28.24.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.14.46.55.38A7.972 7.972 0 0 0 16 8.199a8 8 0 0 0-8-8Z" />
</svg> </svg>
) );
} }
function FeedIcon(props: React.ComponentPropsWithoutRef<'svg'>) { function FeedIcon(props: React.ComponentPropsWithoutRef<"svg">) {
return ( return (
<svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}> <svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}>
<path <path
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"
d="M2.5 3a.5.5 0 0 1 .5-.5h.5c5.523 0 10 4.477 10 10v.5a.5.5 0 0 1-.5.5h-.5a.5.5 0 0 1-.5-.5v-.5A8.5 8.5 0 0 0 3.5 4H3a.5.5 0 0 1-.5-.5V3Zm0 4.5A.5.5 0 0 1 3 7h.5A5.5 5.5 0 0 1 9 12.5v.5a.5.5 0 0 1-.5.5H8a.5.5 0 0 1-.5-.5v-.5a4 4 0 0 0-4-4H3a.5.5 0 0 1-.5-.5v-.5Zm0 5a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z" d="M2.5 3a.5.5 0 0 1 .5-.5h.5c5.523 0 10 4.477 10 10v.5a.5.5 0 0 1-.5.5h-.5a.5.5 0 0 1-.5-.5v-.5A8.5 8.5 0 0 0 3.5 4H3a.5.5 0 0 1-.5-.5V3Zm0 4.5A.5.5 0 0 1 3 7h.5A5.5 5.5 0 0 1 9 12.5v.5a.5.5 0 0 1-.5.5H8a.5.5 0 0 1-.5-.5v-.5a4 4 0 0 0-4-4H3a.5.5 0 0 1-.5-.5v-.5Zm0 5a1 1 0 1 1 2 0 1 1 0 0 1-2 0Z"
/> />
</svg> </svg>
) );
} }
function XIcon(props: React.ComponentPropsWithoutRef<'svg'>) { function XIcon(props: React.ComponentPropsWithoutRef<"svg">) {
return ( return (
<svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}> <svg viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" {...props}>
<path d="M9.51762 6.77491L15.3459 0H13.9648L8.90409 5.88256L4.86212 0H0.200195L6.31244 8.89547L0.200195 16H1.58139L6.92562 9.78782L11.1942 16H15.8562L9.51728 6.77491H9.51762ZM7.62588 8.97384L7.00658 8.08805L2.07905 1.03974H4.20049L8.17706 6.72795L8.79636 7.61374L13.9654 15.0075H11.844L7.62588 8.97418V8.97384Z" /> <path d="M9.51762 6.77491L15.3459 0H13.9648L8.90409 5.88256L4.86212 0H0.200195L6.31244 8.89547L0.200195 16H1.58139L6.92562 9.78782L11.1942 16H15.8562L9.51728 6.77491H9.51762ZM7.62588 8.97384L7.00658 8.08805L2.07905 1.03974H4.20049L8.17706 6.72795L8.79636 7.61374L13.9654 15.0075H11.844L7.62588 8.97418V8.97384Z" />
</svg> </svg>
) );
} }
export function Intro() { export function Intro() {
return ( return (
<> <>
<div className=''> <div className="">
<Link href="/"> <Link href="/">
{/* <Logo className="inline-block h-8 w-auto" /> */} {/* <Logo className="inline-block h-8 w-auto" /> */}
</Link> </Link>
</div> </div>
<h1 className="mt-14 font-sans font-semibold tracking-tighter text-5xl"> <h1 className="mt-14 font-sans font-semibold tracking-tighter text-5xl">
All of the changes made will be {' '} All of the changes made will be{" "}
<span className="">available here.</span> <span className="">available here.</span>
</h1> </h1>
<p className="mt-4 text-sm text-gray-600 dark:text-gray-300"> <p className="mt-4 text-sm text-gray-600 dark:text-gray-300">
Better Auth is advanced authentication library for typescript packed by customizable and extendible plugin ecosystem Better Auth is advanced authentication library for typescript packed by
</p> customizable and extendible plugin ecosystem
<hr className='h-px bg-gray-300 mt-5' /> </p>
<div className="mt-8 flex flex-wrap text-gray-600 dark:text-gray-300 justify-center gap-x-1 gap-y-3 sm:gap-x-2 lg:justify-start"> <hr className="h-px bg-gray-300 mt-5" />
<IconLink href="/docs" icon={BookIcon} className="flex-none text-gray-600 dark:text-gray-300"> <div className="mt-8 flex flex-wrap text-gray-600 dark:text-gray-300 justify-center gap-x-1 gap-y-3 sm:gap-x-2 lg:justify-start">
Documentation <IconLink
</IconLink> href="/docs"
<IconLink href="https://github.com/better-auth/better-auth" icon={GitHubIcon} className="flex-none text-gray-600 dark:text-gray-300"> icon={BookIcon}
GitHub className="flex-none text-gray-600 dark:text-gray-300"
</IconLink> >
<IconLink href="/feed.xml" icon={FeedIcon} className="flex-none text-gray-600 dark:text-gray-300"> Documentation
RSS </IconLink>
</IconLink> <IconLink
</div> href="https://github.com/better-auth/better-auth"
</> icon={GitHubIcon}
) className="flex-none text-gray-600 dark:text-gray-300"
>
GitHub
</IconLink>
<IconLink
href="/feed.xml"
icon={FeedIcon}
className="flex-none text-gray-600 dark:text-gray-300"
>
RSS
</IconLink>
</div>
</>
);
} }
export function IntroFooter() { export function IntroFooter() {
return ( return (
<p className="flex items-baseline gap-x-2 text-[0.8125rem]/6 text-gray-500"> <p className="flex items-baseline gap-x-2 text-[0.8125rem]/6 text-gray-500">
Brought to you by{' '} Brought to you by{" "}
<IconLink href="#" icon={XIcon} compact> <IconLink href="#" icon={XIcon} compact>
BETTER-AUTH. BETTER-AUTH.
</IconLink> </IconLink>
</p> </p>
) );
} }
export function SignUpForm() { export function SignUpForm() {
let id = useId() let id = useId();
return ( return (
<form className="relative isolate mt-8 flex items-center pr-1"> <form className="relative isolate mt-8 flex items-center pr-1">
<label htmlFor={id} className="sr-only"> <label htmlFor={id} className="sr-only">
Email address Email address
</label> </label>
<div className="absolute inset-0 -z-10 rounded-lg transition peer-focus:ring-4 peer-focus:ring-sky-300/15" /> <div className="absolute inset-0 -z-10 rounded-lg transition peer-focus:ring-4 peer-focus:ring-sky-300/15" />
<div className="absolute inset-0 -z-10 rounded-lg bg-white/2.5 ring-1 ring-white/15 transition peer-focus:ring-sky-300" /> <div className="absolute inset-0 -z-10 rounded-lg bg-white/2.5 ring-1 ring-white/15 transition peer-focus:ring-sky-300" />
</form> </form>
) );
} }
export function IconLink({ export function IconLink({
children, children,
className, className,
compact = false, compact = false,
icon: Icon, icon: Icon,
...props ...props
}: React.ComponentPropsWithoutRef<typeof Link> & { }: React.ComponentPropsWithoutRef<typeof Link> & {
compact?: boolean compact?: boolean;
icon?: React.ComponentType<{ className?: string }> icon?: React.ComponentType<{ className?: string }>;
}) { }) {
return ( return (
<Link <Link
{...props} {...props}
className={clsx( className={clsx(
className, className,
'group relative isolate flex items-center rounded-lg px-2 py-0.5 text-[0.8125rem]/6 font-medium text-black/70 dark:text-white/30 transition-colors hover:text-stone-300', "group relative isolate flex items-center rounded-lg px-2 py-0.5 text-[0.8125rem]/6 font-medium text-black/70 dark:text-white/30 transition-colors hover:text-stone-300",
compact ? 'gap-x-2' : 'gap-x-3', compact ? "gap-x-2" : "gap-x-3",
)} )}
> >
<span className="absolute inset-0 -z-10 scale-75 rounded-lg bg-white/5 opacity-0 transition group-hover:scale-100 group-hover:opacity-100" /> <span className="absolute inset-0 -z-10 scale-75 rounded-lg bg-white/5 opacity-0 transition group-hover:scale-100 group-hover:opacity-100" />
{Icon && <Icon className="h-4 w-4 flex-none" />} {Icon && <Icon className="h-4 w-4 flex-none" />}
<span className="self-baseline text-black/70 dark:text-white">{children}</span> <span className="self-baseline text-black/70 dark:text-white">
</Link> {children}
) </span>
</Link>
);
} }

View File

@@ -1,21 +1,25 @@
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const dateFormatter = new Intl.DateTimeFormat('en-US', { const dateFormatter = new Intl.DateTimeFormat("en-US", {
year: 'numeric', year: "numeric",
month: 'short', month: "short",
day: 'numeric', day: "numeric",
timeZone: 'UTC', timeZone: "UTC",
}) });
export function FormattedDate({ export function FormattedDate({
date, date,
...props ...props
}: React.ComponentPropsWithoutRef<'time'> & { date: string | Date }) { }: React.ComponentPropsWithoutRef<"time"> & { date: string | Date }) {
date = typeof date === 'string' ? new Date(date) : date date = typeof date === "string" ? new Date(date) : date;
return ( return (
<time className={cn(props.className , '')} dateTime={date.toISOString()} {...props}> <time
{dateFormatter.format(date)} className={cn(props.className, "")}
</time> dateTime={date.toISOString()}
) {...props}
>
{dateFormatter.format(date)}
</time>
);
} }

View File

@@ -1,221 +1,221 @@
'use client' "use client";
import { useEffect, useId, useRef } from 'react' import { useEffect, useId, useRef } from "react";
import clsx from 'clsx' import clsx from "clsx";
import { type TimelineSegment, animate, timeline } from 'motion' import { type TimelineSegment, animate, timeline } from "motion";
type Star = [x: number, y: number, dim?: boolean, blur?: boolean] type Star = [x: number, y: number, dim?: boolean, blur?: boolean];
const stars: Array<Star> = [ const stars: Array<Star> = [
[4, 4, true, true], [4, 4, true, true],
[4, 44, true], [4, 44, true],
[36, 22], [36, 22],
[50, 146, true, true], [50, 146, true, true],
[64, 43, true, true], [64, 43, true, true],
[76, 30, true], [76, 30, true],
[101, 116], [101, 116],
[140, 36, true], [140, 36, true],
[149, 134], [149, 134],
[162, 74, true], [162, 74, true],
[171, 96, true, true], [171, 96, true, true],
[210, 56, true, true], [210, 56, true, true],
[235, 90], [235, 90],
[275, 82, true, true], [275, 82, true, true],
[306, 6], [306, 6],
[307, 64, true, true], [307, 64, true, true],
[380, 68, true], [380, 68, true],
[380, 108, true, true], [380, 108, true, true],
[391, 148, true, true], [391, 148, true, true],
[405, 18, true], [405, 18, true],
[412, 86, true, true], [412, 86, true, true],
[426, 210, true, true], [426, 210, true, true],
[427, 56, true, true], [427, 56, true, true],
[538, 138], [538, 138],
[563, 88, true, true], [563, 88, true, true],
[611, 154, true, true], [611, 154, true, true],
[637, 150], [637, 150],
[651, 146, true], [651, 146, true],
[682, 70, true, true], [682, 70, true, true],
[683, 128], [683, 128],
[781, 82, true, true], [781, 82, true, true],
[785, 158, true], [785, 158, true],
[832, 146, true, true], [832, 146, true, true],
[852, 89], [852, 89],
] ];
const constellations: Array<Array<Star>> = [ const constellations: Array<Array<Star>> = [
[ [
[247, 103], [247, 103],
[261, 86], [261, 86],
[307, 104], [307, 104],
[357, 36], [357, 36],
], ],
[ [
[586, 120], [586, 120],
[516, 100], [516, 100],
[491, 62], [491, 62],
[440, 107], [440, 107],
[477, 180], [477, 180],
[516, 100], [516, 100],
], ],
[ [
[733, 100], [733, 100],
[803, 120], [803, 120],
[879, 113], [879, 113],
[823, 164], [823, 164],
[803, 120], [803, 120],
], ],
] ];
function Star({ function Star({
blurId, blurId,
point: [cx, cy, dim, blur], point: [cx, cy, dim, blur],
}: { }: {
blurId: string blurId: string;
point: Star point: Star;
}) { }) {
let groupRef = useRef<React.ElementRef<'g'>>(null) let groupRef = useRef<React.ElementRef<"g">>(null);
let ref = useRef<React.ElementRef<'circle'>>(null) let ref = useRef<React.ElementRef<"circle">>(null);
useEffect(() => { useEffect(() => {
if (!groupRef.current || !ref.current) { if (!groupRef.current || !ref.current) {
return return;
} }
let delay = Math.random() * 2 let delay = Math.random() * 2;
let animations = [ let animations = [
animate(groupRef.current, { opacity: 1 }, { duration: 4, delay }), animate(groupRef.current, { opacity: 1 }, { duration: 4, delay }),
animate( animate(
ref.current, ref.current,
{ {
opacity: dim ? [0.2, 0.5] : [1, 0.6], opacity: dim ? [0.2, 0.5] : [1, 0.6],
scale: dim ? [1, 1.2] : [1.2, 1], scale: dim ? [1, 1.2] : [1.2, 1],
}, },
{ {
delay, delay,
duration: Math.random() * 2 + 2, duration: Math.random() * 2 + 2,
direction: 'alternate', direction: "alternate",
repeat: Infinity, repeat: Infinity,
}, },
), ),
] ];
return () => { return () => {
for (let animation of animations) { for (let animation of animations) {
animation.cancel() animation.cancel();
} }
} };
}, [dim]) }, [dim]);
return ( return (
<g ref={groupRef} className="opacity-0"> <g ref={groupRef} className="opacity-0">
<circle <circle
ref={ref} ref={ref}
cx={cx} cx={cx}
cy={cy} cy={cy}
r={1} r={1}
style={{ style={{
transformOrigin: `${cx / 16}rem ${cy / 16}rem`, transformOrigin: `${cx / 16}rem ${cy / 16}rem`,
opacity: dim ? 0.2 : 1, opacity: dim ? 0.2 : 1,
transform: `scale(${dim ? 1 : 1.2})`, transform: `scale(${dim ? 1 : 1.2})`,
}} }}
filter={blur ? `url(#${blurId})` : undefined} filter={blur ? `url(#${blurId})` : undefined}
/> />
</g> </g>
) );
} }
function Constellation({ function Constellation({
points, points,
blurId, blurId,
}: { }: {
points: Array<Star> points: Array<Star>;
blurId: string blurId: string;
}) { }) {
let ref = useRef<React.ElementRef<'path'>>(null) let ref = useRef<React.ElementRef<"path">>(null);
let uniquePoints = points.filter( let uniquePoints = points.filter(
(point, pointIndex) => (point, pointIndex) =>
points.findIndex((p) => String(p) === String(point)) === pointIndex, points.findIndex((p) => String(p) === String(point)) === pointIndex,
) );
let isFilled = uniquePoints.length !== points.length let isFilled = uniquePoints.length !== points.length;
useEffect(() => { useEffect(() => {
if (!ref.current) { if (!ref.current) {
return return;
} }
let sequence: Array<TimelineSegment> = [ let sequence: Array<TimelineSegment> = [
[ [
ref.current, ref.current,
{ strokeDashoffset: 0, visibility: 'visible' }, { strokeDashoffset: 0, visibility: "visible" },
{ duration: 5, delay: Math.random() * 3 + 2 }, { duration: 5, delay: Math.random() * 3 + 2 },
], ],
] ];
if (isFilled) { if (isFilled) {
sequence.push([ sequence.push([
ref.current, ref.current,
{ fill: 'rgb(255 255 255 / 0.02)' }, { fill: "rgb(255 255 255 / 0.02)" },
{ duration: 1 }, { duration: 1 },
]) ]);
} }
let animation = timeline(sequence) let animation = timeline(sequence);
return () => { return () => {
animation.cancel() animation.cancel();
} };
}, [isFilled]) }, [isFilled]);
return ( return (
<> <>
<path <path
ref={ref} ref={ref}
stroke="white" stroke="white"
strokeOpacity="0.2" strokeOpacity="0.2"
strokeDasharray={1} strokeDasharray={1}
strokeDashoffset={1} strokeDashoffset={1}
pathLength={1} pathLength={1}
fill="transparent" fill="transparent"
d={`M ${points.join('L')}`} d={`M ${points.join("L")}`}
className="invisible" className="invisible"
/> />
{uniquePoints.map((point, pointIndex) => ( {uniquePoints.map((point, pointIndex) => (
<Star key={pointIndex} point={point} blurId={blurId} /> <Star key={pointIndex} point={point} blurId={blurId} />
))} ))}
</> </>
) );
} }
export function StarField({ className }: { className?: string }) { export function StarField({ className }: { className?: string }) {
let blurId = useId() let blurId = useId();
return ( return (
<svg <svg
viewBox="0 0 881 211" viewBox="0 0 881 211"
fill="white" fill="white"
aria-hidden="true" aria-hidden="true"
className={clsx( className={clsx(
'pointer-events-none absolute w-[55.0625rem] origin-top-right rotate-[30deg] overflow-visible opacity-70', "pointer-events-none absolute w-[55.0625rem] origin-top-right rotate-[30deg] overflow-visible opacity-70",
className, className,
)} )}
> >
<defs> <defs>
<filter id={blurId}> <filter id={blurId}>
<feGaussianBlur in="SourceGraphic" stdDeviation=".5" /> <feGaussianBlur in="SourceGraphic" stdDeviation=".5" />
</filter> </filter>
</defs> </defs>
{constellations.map((points, constellationIndex) => ( {constellations.map((points, constellationIndex) => (
<Constellation <Constellation
key={constellationIndex} key={constellationIndex}
points={points} points={points}
blurId={blurId} blurId={blurId}
/> />
))} ))}
{stars.map((point, pointIndex) => ( {stars.map((point, pointIndex) => (
<Star key={pointIndex} point={point} blurId={blurId} /> <Star key={pointIndex} point={point} blurId={blurId} />
))} ))}
</svg> </svg>
) );
} }

View File

@@ -5,29 +5,29 @@ const listOfFeatures = ["The first better-auth public beta release"];
const bugFixes = [""]; const bugFixes = [""];
const ChangelogOne = () => { const ChangelogOne = () => {
return ( return (
<AnimatePresence> <AnimatePresence>
<div className="flex flex-col gap-4 items-start justify-center max-w-full md:max-w-2xl"> <div className="flex flex-col gap-4 items-start justify-center max-w-full md:max-w-2xl">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<h2 className="text-2xl font-bold tracking-tighter"> <h2 className="text-2xl font-bold tracking-tighter">
Public Beta Release Public Beta Release
</h2> </h2>
<p> <p>
The first public beta release of better-auth is now available. This The first public beta release of better-auth is now available. This
release includes a lot of new features and improvements. release includes a lot of new features and improvements.
</p> </p>
</div> </div>
<p className="text-gray-600 dark:text-gray-300 text-[0.855rem]"></p> <p className="text-gray-600 dark:text-gray-300 text-[0.855rem]"></p>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<h4 className="text-xl tracking-tighter">Features</h4> <h4 className="text-xl tracking-tighter">Features</h4>
</div> </div>
<ul className="list-disc ml-10 text-[0.855rem] text-gray-600 dark:text-gray-300"> <ul className="list-disc ml-10 text-[0.855rem] text-gray-600 dark:text-gray-300">
{listOfFeatures.map((change, i) => ( {listOfFeatures.map((change, i) => (
<li key={i}>{change}</li> <li key={i}>{change}</li>
))} ))}
</ul> </ul>
</div> </div>
</AnimatePresence> </AnimatePresence>
); );
}; };
export default ChangelogOne; export default ChangelogOne;

View File

@@ -1,8 +1,8 @@
import ChangelogOne from "./2024-09-28"; import ChangelogOne from "./2024-09-28";
export const Logs = [ export const Logs = [
{ {
name: "Changelog 1", name: "Changelog 1",
date: "2024-09-28", date: "2024-09-28",
component: ChangelogOne, component: ChangelogOne,
}, },
]; ];

View File

@@ -5,108 +5,108 @@ import { Intro, IntroFooter } from "./_components/changelog-layout";
import { StarField } from "./_components/stat-field"; import { StarField } from "./_components/stat-field";
function Timeline() { function Timeline() {
let id = useId(); let id = useId();
return ( return (
<div className="pointer-events-none absolute inset-0 z-50 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem] lg:overflow-visible"> <div className="pointer-events-none absolute inset-0 z-50 overflow-hidden lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem] lg:overflow-visible">
<svg <svg
className="absolute left-[max(0px,calc(50%-18.125rem))] top-0 h-full w-1.5 lg:left-full lg:ml-1 xl:left-auto xl:right-1 xl:ml-0" className="absolute left-[max(0px,calc(50%-18.125rem))] top-0 h-full w-1.5 lg:left-full lg:ml-1 xl:left-auto xl:right-1 xl:ml-0"
aria-hidden="true" aria-hidden="true"
> >
<defs> <defs>
<pattern id={id} width="6" height="8" patternUnits="userSpaceOnUse"> <pattern id={id} width="6" height="8" patternUnits="userSpaceOnUse">
<path <path
d="M0 0H6M0 8H6" d="M0 0H6M0 8H6"
className="stroke-sky-900/10 xl:stroke-white/10 dark:stroke-white/10" className="stroke-sky-900/10 xl:stroke-white/10 dark:stroke-white/10"
fill="none" fill="none"
/> />
</pattern> </pattern>
</defs> </defs>
<rect width="100%" height="100%" fill={`url(#${id})`} /> <rect width="100%" height="100%" fill={`url(#${id})`} />
</svg> </svg>
</div> </div>
); );
} }
function Glow() { function Glow() {
let id = useId(); let id = useId();
return ( return (
<div className="absolute inset-0 -z-10 overflow-hidden bg-gradient-to-tr from-transparent via-stone-950/5 to-transparent/10 lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem]"> <div className="absolute inset-0 -z-10 overflow-hidden bg-gradient-to-tr from-transparent via-stone-950/5 to-transparent/10 lg:right-[calc(max(2rem,50%-38rem)+40rem)] lg:min-w-[32rem]">
<svg <svg
className="absolute -bottom-48 left-[-40%] h-[80rem] w-[180%] lg:-right-40 lg:bottom-auto lg:left-auto lg:top-[-40%] lg:h-[180%] lg:w-[80rem]" className="absolute -bottom-48 left-[-40%] h-[80rem] w-[180%] lg:-right-40 lg:bottom-auto lg:left-auto lg:top-[-40%] lg:h-[180%] lg:w-[80rem]"
aria-hidden="true" aria-hidden="true"
> >
<defs> <defs>
<radialGradient id={`${id}-desktop`} cx="100%"> <radialGradient id={`${id}-desktop`} cx="100%">
<stop offset="0%" stopColor="rgba(41, 37, 36, 0.4)" /> <stop offset="0%" stopColor="rgba(41, 37, 36, 0.4)" />
<stop offset="53.95%" stopColor="rgba(28, 25, 23, 0.09)" /> <stop offset="53.95%" stopColor="rgba(28, 25, 23, 0.09)" />
<stop offset="100%" stopColor="rgba(0, 0, 0, 0)" /> <stop offset="100%" stopColor="rgba(0, 0, 0, 0)" />
</radialGradient> </radialGradient>
<radialGradient id={`${id}-mobile`} cy="100%"> <radialGradient id={`${id}-mobile`} cy="100%">
<stop offset="0%" stopColor="rgba(41, 37, 36, 0.3)" /> <stop offset="0%" stopColor="rgba(41, 37, 36, 0.3)" />
<stop offset="53.95%" stopColor="rgba(28, 25, 23, 0.09)" /> <stop offset="53.95%" stopColor="rgba(28, 25, 23, 0.09)" />
<stop offset="100%" stopColor="rgba(0, 0, 0, 0)" /> <stop offset="100%" stopColor="rgba(0, 0, 0, 0)" />
</radialGradient> </radialGradient>
</defs> </defs>
<rect <rect
width="100%" width="100%"
height="100%" height="100%"
fill={`url(#${id}-desktop)`} fill={`url(#${id}-desktop)`}
className="hidden lg:block" className="hidden lg:block"
/> />
<rect <rect
width="100%" width="100%"
height="100%" height="100%"
fill={`url(#${id}-mobile)`} fill={`url(#${id}-mobile)`}
className="lg:hidden" className="lg:hidden"
/> />
</svg> </svg>
<div className="absolute inset-x-0 bottom-0 right-0 h-px bg-white/5 mix-blend-overlay lg:left-auto lg:top-0 lg:h-auto lg:w-px" /> <div className="absolute inset-x-0 bottom-0 right-0 h-px bg-white/5 mix-blend-overlay lg:left-auto lg:top-0 lg:h-auto lg:w-px" />
</div> </div>
); );
} }
function FixedSidebar({ function FixedSidebar({
main, main,
footer, footer,
}: { }: {
main: React.ReactNode; main: React.ReactNode;
footer: React.ReactNode; footer: React.ReactNode;
}) { }) {
return ( return (
<div className="relative flex-none overflow-hidden px-10 lg:pointer-events-none md:fixed lg:inset-0 lg:z-40 lg:flex lg:px-0"> <div className="relative flex-none overflow-hidden px-10 lg:pointer-events-none md:fixed lg:inset-0 lg:z-40 lg:flex lg:px-0">
<Glow /> <Glow />
<div className="relative flex w-full lg:pointer-events-auto lg:mr-[calc(max(2rem,50%-35rem)+40rem)] lg:min-w-[32rem] lg:overflow-y-auto lg:overflow-x-hidden lg:pl-[max(4rem,calc(50%-44rem))]"> <div className="relative flex w-full lg:pointer-events-auto lg:mr-[calc(max(2rem,50%-35rem)+40rem)] lg:min-w-[32rem] lg:overflow-y-auto lg:overflow-x-hidden lg:pl-[max(4rem,calc(50%-44rem))]">
<div className="mx-auto pr-20 max-w-lg lg:mx-auto lg:flex lg:max-w-4xl lg:flex-col lg:before:flex-1 lg:before:pt-6"> <div className="mx-auto pr-20 max-w-lg lg:mx-auto lg:flex lg:max-w-4xl lg:flex-col lg:before:flex-1 lg:before:pt-6">
<div className="pb-16 pt-20 sm:pb-20 sm:pt-32 lg:py-20"> <div className="pb-16 pt-20 sm:pb-20 sm:pt-32 lg:py-20">
<div className="relative "> <div className="relative ">
<StarField className="-right-44 top-14" /> <StarField className="-right-44 top-14" />
{main} {main}
</div> </div>
</div> </div>
<div className="flex flex-1 items-end justify-center pb-4 lg:justify-start lg:pb-6"> <div className="flex flex-1 items-end justify-center pb-4 lg:justify-start lg:pb-6">
{footer} {footer}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }
const ChangeLogLayout = ({ children }: { children: React.ReactNode }) => { const ChangeLogLayout = ({ children }: { children: React.ReactNode }) => {
return ( return (
<div className="h-full font-sans overflow-y-auto"> <div className="h-full font-sans overflow-y-auto">
<FixedSidebar main={<Intro />} footer={<IntroFooter />} /> <FixedSidebar main={<Intro />} footer={<IntroFooter />} />
<div /> <div />
<div className="relative overflow-y-auto flex gap-2 justify-start items-start"> <div className="relative overflow-y-auto flex gap-2 justify-start items-start">
<Timeline /> <Timeline />
<div className="space-y-20 py-20 w-[80%] mx-auto md:mx-0 md:ml-auto md:w-1/2 h-screen sm:space-y-20 sm:py-20"> <div className="space-y-20 py-20 w-[80%] mx-auto md:mx-0 md:ml-auto md:w-1/2 h-screen sm:space-y-20 sm:py-20">
{children} {children}
</div> </div>
</div> </div>
</div> </div>
); );
}; };
export default ChangeLogLayout; export default ChangeLogLayout;

View File

@@ -2,24 +2,24 @@ import { AnimatePresence } from "@/components/ui/fade-in";
import { Logs } from "./_logs"; import { Logs } from "./_logs";
import { FormattedDate } from "./_components/fmt-dates"; import { FormattedDate } from "./_components/fmt-dates";
const ChangelogPage = () => { const ChangelogPage = () => {
return ( return (
<div> <div>
<div className="mt-10 overflow-visible h-full flex flex-col gap-10"> <div className="mt-10 overflow-visible h-full flex flex-col gap-10">
{Logs.map((log) => { {Logs.map((log) => {
return ( return (
<div className="relative my-5 h-auto"> <div className="relative my-5 h-auto">
<div className="sticky top-2 flex-1 h-full"> <div className="sticky top-2 flex-1 h-full">
<FormattedDate <FormattedDate
className="absolute md:-left-32 left-0 text-sm -top-8 md:top-0 font-light" className="absolute md:-left-32 left-0 text-sm -top-8 md:top-0 font-light"
date={log.date} date={log.date}
/> />
</div> </div>
<log.component /> <log.component />
</div> </div>
); );
})} })}
</div> </div>
</div> </div>
); );
}; };
export default ChangelogPage; export default ChangelogPage;

View File

@@ -4,14 +4,14 @@ import { docsOptions } from "../layout.config";
import ArticleLayout from "@/components/side-bar"; import ArticleLayout from "@/components/side-bar";
export default function Layout({ children }: { children: ReactNode }) { export default function Layout({ children }: { children: ReactNode }) {
return ( return (
<DocsLayout <DocsLayout
{...docsOptions} {...docsOptions}
sidebar={{ sidebar={{
component: <ArticleLayout />, component: <ArticleLayout />,
}} }}
> >
{children} {children}
</DocsLayout> </DocsLayout>
); );
} }

View File

@@ -9,45 +9,45 @@ import { baseUrl, createMetadata } from "@/lib/metadata";
import { Banner } from "fumadocs-ui/components/banner"; import { Banner } from "fumadocs-ui/components/banner";
import Link from "next/link"; import Link from "next/link";
export const metadata = createMetadata({ export const metadata = createMetadata({
title: { title: {
template: "%s | Better Auth", template: "%s | Better Auth",
default: "Better Auth", default: "Better Auth",
}, },
description: "The authentication library for typescript", description: "The authentication library for typescript",
metadataBase: baseUrl, metadataBase: baseUrl,
}); });
export default function Layout({ children }: { children: ReactNode }) { export default function Layout({ children }: { children: ReactNode }) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<head> <head>
<link rel="icon" href="/favicon/favicon.ico" sizes="any" /> <link rel="icon" href="/favicon/favicon.ico" sizes="any" />
</head> </head>
<body <body
className={`${GeistSans.variable} ${GeistMono.variable} font-sans overflow-x-hidden`} className={`${GeistSans.variable} ${GeistMono.variable} font-sans overflow-x-hidden`}
> >
<Banner <Banner
id="public-beta" id="public-beta"
className="lg:text-sm tracking-tight text-xs hidden md:flex bg-gradient-to-tr from-white to-stone-100 border dark:from-stone-900 dark:to-stone-950" className="lg:text-sm tracking-tight text-xs hidden md:flex bg-gradient-to-tr from-white to-stone-100 border dark:from-stone-900 dark:to-stone-950"
> >
🚧 Heads up! We're still in beta. It isn't quite production-ready just 🚧 Heads up! We're still in beta. It isn't quite production-ready just
yet. If you run into any bugs or quirks, please report them on{" "} yet. If you run into any bugs or quirks, please report them on{" "}
<Link <Link
target="_blank" target="_blank"
className="mx-1 underline pb-px hover:opacity-80 transition-all" className="mx-1 underline pb-px hover:opacity-80 transition-all"
href="https://github.com/better-auth/better-auth/issues" href="https://github.com/better-auth/better-auth/issues"
> >
{" "} {" "}
Github. Github.
</Link>{" "} </Link>{" "}
</Banner> </Banner>
<RootProvider> <RootProvider>
<NavbarProvider> <NavbarProvider>
<Navbar /> <Navbar />
{children} {children}
</NavbarProvider> </NavbarProvider>
</RootProvider> </RootProvider>
</body> </body>
</html> </html>
); );
} }

View File

@@ -13,8 +13,7 @@ export default function HomePage() {
> >
<Hero /> <Hero />
<Features /> <Features />
<hr className="h-px bg-gray-200"/> <hr className="h-px bg-gray-200" />
</Section> </Section>
</main> </main>
); );

View File

@@ -1,11 +1,11 @@
import badgestyle from './badge.module.css' import badgestyle from "./badge.module.css";
export const PulicBetaBadge = ({ text }: { text?: string }) => { export const PulicBetaBadge = ({ text }: { text?: string }) => {
return ( return (
<div className={badgestyle.beta}> <div className={badgestyle.beta}>
<span className={badgestyle.top_key}></span> <span className={badgestyle.top_key}></span>
<span className={badgestyle.text}>{text || "BETA"}</span> <span className={badgestyle.text}>{text || "BETA"}</span>
<span className={badgestyle.bottom_key_1}></span> <span className={badgestyle.bottom_key_1}></span>
<span className={badgestyle.bottom_key_2}></span> <span className={badgestyle.bottom_key_2}></span>
</div> </div>
) );
} };

View File

@@ -2,135 +2,135 @@ import React from "react";
import { useId } from "react"; import { useId } from "react";
export function Features() { export function Features() {
return ( return (
<div className="py-2"> <div className="py-2">
<div className="mt-2 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-10 md:gap-2 max-w-7xl mx-auto"> <div className="mt-2 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-10 md:gap-2 max-w-7xl mx-auto">
{grid.map((feature, i) => ( {grid.map((feature, i) => (
<div <div
key={feature.title} key={feature.title}
className="relative bg-gradient-to-b dark:from-neutral-900 from-neutral-100 dark:to-neutral-950 to-white px-6 py-2 overflow-hidden" className="relative bg-gradient-to-b dark:from-neutral-900 from-neutral-100 dark:to-neutral-950 to-white px-6 py-2 overflow-hidden"
> >
<Grid size={i * 5 + 10} /> <Grid size={i * 5 + 10} />
<p className="text-base font-bold text-neutral-800 dark:text-white relative z-20"> <p className="text-base font-bold text-neutral-800 dark:text-white relative z-20">
{feature.title} {feature.title}
</p> </p>
<p className="text-neutral-600 dark:text-neutral-400 text-base font-normal relative z-20"> <p className="text-neutral-600 dark:text-neutral-400 text-base font-normal relative z-20">
{feature.description} {feature.description}
</p> </p>
</div> </div>
))} ))}
</div> </div>
</div> </div>
); );
} }
const grid = [ const grid = [
{ {
title: "Framework Agnostic", title: "Framework Agnostic",
description: "Support for most popular frameworks", description: "Support for most popular frameworks",
}, },
{ {
title: "Email & Password", title: "Email & Password",
description: description:
"Built-in support for secure email and password authentication", "Built-in support for secure email and password authentication",
}, },
{ {
title: "Account & Session Management", title: "Account & Session Management",
description: "Manage user accounts and sessions with ease", description: "Manage user accounts and sessions with ease",
}, },
{ {
title: "Built In Rate Limiter", title: "Built In Rate Limiter",
description: "Built in rate limiter with custom rules", description: "Built in rate limiter with custom rules",
}, },
{ {
title: "Automatic Database Management", title: "Automatic Database Management",
description: "Automatic database management and migrations", description: "Automatic database management and migrations",
}, },
{ {
title: "Social Sign-on", title: "Social Sign-on",
description: "multiple social sign-on providers", description: "multiple social sign-on providers",
}, },
{ {
title: "Organization & Access Control", title: "Organization & Access Control",
description: "Manage organizations and access control", description: "Manage organizations and access control",
}, },
{ {
title: "Two Factor Authentication", title: "Two Factor Authentication",
description: "Secure your users with two factor authentication", description: "Secure your users with two factor authentication",
}, },
{ {
title: "Plugin Ecosystem", title: "Plugin Ecosystem",
description: "Even more capabilities with plugins", description: "Even more capabilities with plugins",
}, },
]; ];
export const Grid = ({ export const Grid = ({
pattern, pattern,
size, size,
}: { }: {
pattern?: number[][]; pattern?: number[][];
size?: number; size?: number;
}) => { }) => {
const p = pattern ?? [ const p = pattern ?? [
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
]; ];
return ( return (
<div className="pointer-events-none absolute left-1/2 top-0 -ml-20 -mt-2 h-full w-full [mask-image:linear-gradient(white,transparent)]"> <div className="pointer-events-none absolute left-1/2 top-0 -ml-20 -mt-2 h-full w-full [mask-image:linear-gradient(white,transparent)]">
<div className="absolute inset-0 bg-gradient-to-r [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-zinc-900/30 from-zinc-100/30 to-zinc-300/30 dark:to-zinc-900/30 opacity-100"> <div className="absolute inset-0 bg-gradient-to-r [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-zinc-900/30 from-zinc-100/30 to-zinc-300/30 dark:to-zinc-900/30 opacity-100">
<GridPattern <GridPattern
width={size ?? 20} width={size ?? 20}
height={size ?? 20} height={size ?? 20}
x="-12" x="-12"
y="4" y="4"
squares={p} squares={p}
className="absolute inset-0 h-full w-full mix-blend-overlay dark:fill-white/10 dark:stroke-white/10 stroke-black/10 fill-black/10" className="absolute inset-0 h-full w-full mix-blend-overlay dark:fill-white/10 dark:stroke-white/10 stroke-black/10 fill-black/10"
/> />
</div> </div>
</div> </div>
); );
}; };
export function GridPattern({ width, height, x, y, squares, ...props }: any) { export function GridPattern({ width, height, x, y, squares, ...props }: any) {
const patternId = useId(); const patternId = useId();
return ( return (
<svg aria-hidden="true" {...props}> <svg aria-hidden="true" {...props}>
<defs> <defs>
<pattern <pattern
id={patternId} id={patternId}
width={width} width={width}
height={height} height={height}
patternUnits="userSpaceOnUse" patternUnits="userSpaceOnUse"
x={x} x={x}
y={y} y={y}
> >
<path d={`M.5 ${height}V.5H${width}`} fill="none" /> <path d={`M.5 ${height}V.5H${width}`} fill="none" />
</pattern> </pattern>
</defs> </defs>
<rect <rect
width="100%" width="100%"
height="100%" height="100%"
strokeWidth={0} strokeWidth={0}
fill={`url(#${patternId})`} fill={`url(#${patternId})`}
/> />
{squares && ( {squares && (
<svg x={x} y={y} className="overflow-visible"> <svg x={x} y={y} className="overflow-visible">
{squares.map(([x, y]: any) => ( {squares.map(([x, y]: any) => (
<rect <rect
strokeWidth="0" strokeWidth="0"
key={`${x}-${y}`} key={`${x}-${y}`}
width={width + 1} width={width + 1}
height={height + 1} height={height + 1}
x={x * width} x={x * width}
y={y * height} y={y * height}
/> />
))} ))}
</svg> </svg>
)} )}
</svg> </svg>
); );
} }

View File

@@ -1,42 +1,42 @@
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { techStackIcons } from "./techstack-icons"; import { techStackIcons } from "./techstack-icons";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
export const TechStackDisplay = ({ export const TechStackDisplay = ({
skills, skills,
className, className,
}: { }: {
skills: string[]; skills: string[];
className?: string; className?: string;
}) => { }) => {
return ( return (
<div <div
className={cn( className={cn(
"flex gap-7 flex-wrap mt-3 justify-center items-center max-w-4xl", "flex gap-7 flex-wrap mt-3 justify-center items-center max-w-4xl",
className className,
)} )}
> >
{skills.map((icon) => { {skills.map((icon) => {
return ( return (
<TooltipProvider key={icon}> <TooltipProvider key={icon}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span className="transform duration-300 hover:rotate-12 transition-transform"> <span className="transform duration-300 hover:rotate-12 transition-transform">
{techStackIcons[icon].icon} {techStackIcons[icon].icon}
</span> </span>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent className="text-white/80 bg-gradient-to-tr from-black/80 via-black to-black/90 z-1"> <TooltipContent className="text-white/80 bg-gradient-to-tr from-black/80 via-black to-black/90 z-1">
{techStackIcons[icon].name} {techStackIcons[icon].name}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
); );
})} })}
</div> </div>
); );
}; };

View File

@@ -1,14 +1,14 @@
"use client"; "use client";
import { import {
CheckIcon, CheckIcon,
Globe2Icon, Globe2Icon,
PlugIcon, PlugIcon,
PlugZap2Icon, PlugZap2Icon,
Plus, Plus,
RabbitIcon, RabbitIcon,
ShieldCheckIcon, ShieldCheckIcon,
Webhook, Webhook,
XIcon, XIcon,
} from "lucide-react"; } from "lucide-react";
import { LockClosedIcon } from "@radix-ui/react-icons"; import { LockClosedIcon } from "@radix-ui/react-icons";
import Link from "next/link"; import Link from "next/link";
@@ -18,193 +18,193 @@ import { TechStackDisplay } from "./display-techstack";
import { Ripple } from "./ripple"; import { Ripple } from "./ripple";
export default function Features() { export default function Features() {
return ( return (
<div className="md:w-10/12 mt-10 mx-auto font-geist relative md:border-l-0 md:border-[1.2px] rounded-none -pr-2"> <div className="md:w-10/12 mt-10 mx-auto font-geist relative md:border-l-0 md:border-[1.2px] rounded-none -pr-2">
<Plus className="absolute top-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" /> <Plus className="absolute top-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" />
<div className="grid grid-cols-1 md:grid-cols-3 md:mx-0 grid-rows-4 md:grid-rows-4 w-full"> <div className="grid grid-cols-1 md:grid-cols-3 md:mx-0 grid-rows-4 md:grid-rows-4 w-full">
<div className="relative items-start justify-start border-l-[1.2px] border-t-[1.2px] md:border-t-0 transform-gpu flex flex-col p-10 overflow-clip"> <div className="relative items-start justify-start border-l-[1.2px] border-t-[1.2px] md:border-t-0 transform-gpu flex flex-col p-10 overflow-clip">
<Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" /> <Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" />
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<PlugZap2Icon className="w-4 h-4" /> <PlugZap2Icon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400"> <p className="text-gray-600 dark:text-gray-400">
Framework Agnostic{" "} Framework Agnostic{" "}
</p> </p>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-xl md:text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-xl md:text-2xl tracking-tighter font-normal max-w-lg">
Supports popular <strong>frameworks</strong> Supports popular <strong>frameworks</strong>
</p> </p>
</div> </div>
</div> </div>
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
Supports your favorite frontend, backend and meta frameworks, Supports your favorite frontend, backend and meta frameworks,
including React, Vue, Svelte, Astro, Solid, Next.js, Nuxt.js, including React, Vue, Svelte, Astro, Solid, Next.js, Nuxt.js,
Hono, and more{" "} Hono, and more{" "}
<a className="text-gray-50" href="/docs" target="_blank"> <a className="text-gray-50" href="/docs" target="_blank">
Learn more Learn more
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div className="relative items-start justify-start border-l-[1.2px] border-t-[1.2px] md:border-t-0 transform-gpu flex flex-col p-10"> <div className="relative items-start justify-start border-l-[1.2px] border-t-[1.2px] md:border-t-0 transform-gpu flex flex-col p-10">
<Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" /> <Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" />
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<LockClosedIcon className="w-4 h-4" /> <LockClosedIcon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400">Authentication</p> <p className="text-gray-600 dark:text-gray-400">Authentication</p>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-2xl tracking-tighter font-normal max-w-lg">
Email & Password <strong>Authentication</strong> Email & Password <strong>Authentication</strong>
</p> </p>
</div> </div>
</div> </div>
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
Builtin support for email and password authentication, with secure Builtin support for email and password authentication, with secure
password hashing and account management features{" "} password hashing and account management features{" "}
<a className="text-gray-50" href="/docs" target="_blank"> <a className="text-gray-50" href="/docs" target="_blank">
Learn more Learn more
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div className="relative items-start justify-start md:border-l-[0.2px] border-t-[1.2px] md:border-t-0 flex flex-col p-10"> <div className="relative items-start justify-start md:border-l-[0.2px] border-t-[1.2px] md:border-t-0 flex flex-col p-10">
<Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" /> <Plus className="absolute bottom-[-17px] left-[-17px] text-black/20 dark:text-white/30 w-8 h-8" />
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<Webhook className="w-4 h-4" /> <Webhook className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400">Social Sign-on</p> <p className="text-gray-600 dark:text-gray-400">Social Sign-on</p>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-2xl tracking-tighter font-normal max-w-lg">
Support multiple <strong>OAuth providers.</strong> Support multiple <strong>OAuth providers.</strong>
</p> </p>
</div> </div>
</div> </div>
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
Allow users to sign in with their accounts, including Github, Allow users to sign in with their accounts, including Github,
Google, Discord, Twitter, and more.{" "} Google, Discord, Twitter, and more.{" "}
<a className="text-gray-50" href="#" target="_blank"> <a className="text-gray-50" href="#" target="_blank">
Learn more Learn more
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div className="items-start justify-start border-l-[1.2px] border-t-[1.2px] flex flex-col p-10 "> <div className="items-start justify-start border-l-[1.2px] border-t-[1.2px] flex flex-col p-10 ">
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<ShieldCheckIcon className="w-4 h-4" /> <ShieldCheckIcon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400">Two Factor</p> <p className="text-gray-600 dark:text-gray-400">Two Factor</p>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-2xl tracking-tighter font-normal max-w-lg">
Two Factor <strong>Authentication</strong> Two Factor <strong>Authentication</strong>
</p> </p>
</div> </div>
</div> </div>
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
With our built-in two factor authentication plugin, you can add an With our built-in two factor authentication plugin, you can add an
extra layer of security to your account.{" "} extra layer of security to your account.{" "}
<Link className="text-gray-50" href="/docs" target="_blank"> <Link className="text-gray-50" href="/docs" target="_blank">
Learn more Learn more
</Link> </Link>
</p> </p>
</div> </div>
</div> </div>
<div className="items-start justify-staart border-l-[1.2px] border-t-[1.2px] flex flex-col p-10 "> <div className="items-start justify-staart border-l-[1.2px] border-t-[1.2px] flex flex-col p-10 ">
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<RabbitIcon className="w-4 h-4" /> <RabbitIcon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400"> <p className="text-gray-600 dark:text-gray-400">
Organization & Access Control{" "} Organization & Access Control{" "}
</p> </p>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-2xl tracking-tighter font-normal max-w-lg">
Gain and manage <strong>access.</strong> Gain and manage <strong>access.</strong>
</p> </p>
</div> </div>
</div> </div>
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
Manage users and their access to resources within your Manage users and their access to resources within your
application.{" "} application.{" "}
<a className="text-gray-50" href="/docs" target="_blank"> <a className="text-gray-50" href="/docs" target="_blank">
Learn more Learn more
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div className="items-start justify-start border-l-[1.2px] border-t-[1.2px] transform-gpu relative flex flex-col p-10 "> <div className="items-start justify-start border-l-[1.2px] border-t-[1.2px] transform-gpu relative flex flex-col p-10 ">
<Plus className="absolute bottom-[-15px] right-[-15px] text-black/20 dark:text-white/40 w-8 h-8" /> <Plus className="absolute bottom-[-15px] right-[-15px] text-black/20 dark:text-white/40 w-8 h-8" />
<div className="flex gap-2 items-center my-1"> <div className="flex gap-2 items-center my-1">
<PlugIcon className="w-4 h-4" /> <PlugIcon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400"> <p className="text-gray-600 dark:text-gray-400">
Plugin Ecosystem{" "} Plugin Ecosystem{" "}
</p> </p>
</div> </div>
<div className="max-w-full"> <div className="max-w-full">
<div className="flex gap-3 "> <div className="flex gap-3 ">
<p className="text-2xl tracking-tighter font-normal max-w-lg"> <p className="text-2xl tracking-tighter font-normal max-w-lg">
Pluggable with <strong>custom.</strong> Pluggable with <strong>custom.</strong>
</p> </p>
</div> </div>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<p className="text-left text-sm mt-2 text-muted-foreground"> <p className="text-left text-sm mt-2 text-muted-foreground">
Enhance your application with our official plugins and those Enhance your application with our official plugins and those
created by the community.{" "} created by the community.{" "}
<a className="text-gray-50" href="/docs" target="_blank"> <a className="text-gray-50" href="/docs" target="_blank">
Learn more Learn more
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div className="relative md:grid md:col-span-3 grid-cols-2 row-span-2 border-t-[1.2px] border-l-[1.2px] md:border-b-[1.2px] dark:border-b-0 h-full py-20 "> <div className="relative md:grid md:col-span-3 grid-cols-2 row-span-2 border-t-[1.2px] border-l-[1.2px] md:border-b-[1.2px] dark:border-b-0 h-full py-20 ">
<Plus className="absolute top-[-15px] left-[-15px] text-black/20 dark:text-black/20 w-8 h-8 " /> <Plus className="absolute top-[-15px] left-[-15px] text-black/20 dark:text-black/20 w-8 h-8 " />
<Plus className="absolute top-[-15px] right-[-15px] text-black/20 dark:text-black/20 w-8 h-8 " /> <Plus className="absolute top-[-15px] right-[-15px] text-black/20 dark:text-black/20 w-8 h-8 " />
<div className="p-16 pt-10 md:px-10 h-full md:absolute top-0 left-0 w-full"> <div className="p-16 pt-10 md:px-10 h-full md:absolute top-0 left-0 w-full">
<div className="flex flex-col gap-3 justify-center h-full items-center w-full"> <div className="flex flex-col gap-3 justify-center h-full items-center w-full">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<Globe2Icon className="w-4 h-4" /> <Globe2Icon className="w-4 h-4" />
<p className="text-gray-600 dark:text-gray-400"> <p className="text-gray-600 dark:text-gray-400">
Own your auth Own your auth
</p> </p>
</div> </div>
<p className="text-4xl md:text-4xl mt-4 tracking-tighter font-normal max-w-md mx-auto text-center"> <p className="text-4xl md:text-4xl mt-4 tracking-tighter font-normal max-w-md mx-auto text-center">
<strong>Roll your own auth with confidence in minutes!</strong> <strong>Roll your own auth with confidence in minutes!</strong>
</p> </p>
<div className="flex mt-[10px] z-20 justify-center items-start"> <div className="flex mt-[10px] z-20 justify-center items-start">
<TechStackDisplay <TechStackDisplay
skills={[ skills={[
"nextJs", "nextJs",
"nuxt", "nuxt",
"svelteKit", "svelteKit",
"astro", "astro",
"solidStart", "solidStart",
"react", "react",
"hono", "hono",
]} ]}
/> />
</div> </div>
<Link href="/docs" className="z-50"> <Link href="/docs" className="z-50">
<Button className="mt-4 rounded-none p-5 ml-auto"> <Button className="mt-4 rounded-none p-5 ml-auto">
Start now! Start now!
</Button> </Button>
</Link> </Link>
<Ripple /> <Ripple />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }

Some files were not shown because too many files have changed in this diff Show More