mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 04:19:32 +00:00
chore: lint project
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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‘ve declined the invitation to join{" "}
|
You‘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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { toNextJsHandler } from "better-auth/next-js";
|
import { toNextJsHandler } from "better-auth/next-js";
|
||||||
|
|
||||||
export const { GET, POST } = toNextJsHandler(auth);
|
export const { GET, POST } = toNextJsHandler(auth);
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -5,5 +5,3 @@ export const client = createAuthClient({
|
|||||||
baseURL: "http://localhost:3000/api/auth",
|
baseURL: "http://localhost:3000/api/auth",
|
||||||
plugins: [organizationClient()],
|
plugins: [organizationClient()],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
</>
|
||||||
</>
|
);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user