shadcn 업데이트 && email components
This commit is contained in:
parent
ae00228de3
commit
d7475dc0eb
60 changed files with 1282 additions and 565 deletions
|
|
@ -1,70 +1,157 @@
|
|||
import { Github, Instagram, MailIcon, Rss } from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
"use client";
|
||||
import { MailIcon, Rss, GitBranch, Copy, Check } from "lucide-react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogDescription, DialogFooter, DialogClose, DialogTitle } from "./ui/dialog";
|
||||
import React from "react";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
import {
|
||||
Item,
|
||||
ItemActions,
|
||||
ItemContent,
|
||||
ItemMedia,
|
||||
ItemTitle,
|
||||
} from "@/components/ui/item"
|
||||
|
||||
type ContactMethod = {
|
||||
name: string;
|
||||
icon: React.ReactNode;
|
||||
url?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
type EmailContact = {
|
||||
label: string;
|
||||
address: string;
|
||||
};
|
||||
|
||||
const contact = [
|
||||
{
|
||||
"name": "Email",
|
||||
"url": "mailto:me@imnya.ng",
|
||||
"icon": <MailIcon className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
"name": "Blog",
|
||||
"url": "https://blog.imnya.ng",
|
||||
"icon": <Rss className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/imnyang",
|
||||
"icon": <Github className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
"name": "Instagram",
|
||||
"url": "https://instagram.com/imnya.ng",
|
||||
"icon": <Instagram className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
"name": "𝕏",
|
||||
"url": "https://x.com/imnya_ng",
|
||||
"icon": <p className="text-[20px] font-bold">𝕏</p>
|
||||
},
|
||||
{
|
||||
"name": "Discord",
|
||||
"url": "https://discord.gg/uxxn2ZZHGn",
|
||||
"icon": <Image src="/icon/discord.svg" alt="Discord" width={20} height={20} className="w-5 h-5 invert-0 dark:invert" />
|
||||
},
|
||||
{
|
||||
"name": "maishift",
|
||||
"url": "https://mai.sft.sh/imnyang",
|
||||
"icon": <Image src="/icon/maimai.webp" alt="mai.sft.sh" width={20} height={20} className="w-5 h-5 invert-0 dark:invert" />
|
||||
}
|
||||
]
|
||||
|
||||
export default function Contact() {
|
||||
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
||||
const [copiedEmail, setCopiedEmail] = React.useState<string | null>(null);
|
||||
const emailContacts: EmailContact[] = [
|
||||
{ label: "Personal", address: "me@imnya.ng" },
|
||||
{ label: "ADOFAI.gg", address: "imnyang@adofai.gg" },
|
||||
{ label: "Dazzle.st", address: "imnyang@dazzle.st" },
|
||||
];
|
||||
|
||||
const contact: ContactMethod[] = [
|
||||
{
|
||||
"name": "Email",
|
||||
"onClick": () => setIsDialogOpen(true),
|
||||
"icon": <MailIcon className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
"name": "Blog",
|
||||
"url": "https://blog.imnya.ng",
|
||||
"icon": <Rss className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/imnyang",
|
||||
"icon": <Image src="/icon/github.svg" width={24} height={24} alt="GitHub" className="w-6 h-6" />
|
||||
},
|
||||
{
|
||||
"name": "git.mizuki.guru",
|
||||
"url": "https://git.mizuki.guru/imnyang",
|
||||
"icon": <GitBranch className="w-5 h-5" />
|
||||
},
|
||||
{
|
||||
"name": "Instagram",
|
||||
"url": "https://instagram.com/imnya.ng",
|
||||
"icon": <Image src="/icon/instagram.svg" width={18} height={18} alt="Instagram" className="w-4 h-4" />
|
||||
},
|
||||
{
|
||||
"name": "𝕏",
|
||||
"url": "https://x.com/imnya_ng",
|
||||
"icon": <p className="text-[20px] font-bold">𝕏</p>
|
||||
}
|
||||
]
|
||||
|
||||
const handleClick = (method: ContactMethod) => {
|
||||
if (method.onClick) {
|
||||
method.onClick();
|
||||
} else if (method.url) {
|
||||
window.open(method.url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
};
|
||||
|
||||
const copyEmail = (email: string) => {
|
||||
void navigator.clipboard.writeText(email);
|
||||
setCopiedEmail(email);
|
||||
|
||||
setTimeout(() => {
|
||||
setCopiedEmail((currentEmail) => (currentEmail === email ? null : currentEmail));
|
||||
}, 1200);
|
||||
};
|
||||
|
||||
const openMailTo = (email: string) => {
|
||||
window.location.href = `mailto:${email}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row space-x-4">
|
||||
{contact.map((method) => (
|
||||
<Tooltip key={method.name} >
|
||||
<Tooltip key={method.name}>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href={method.url}
|
||||
className="flex items-center space-x-2 text-foreground/80 hover:text-foreground transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleClick(method)}
|
||||
className="flex items-center space-x-2 text-foreground/80 hover:text-foreground transition-colors hover:cursor-pointer"
|
||||
>
|
||||
{method.icon}
|
||||
</Link>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{method.name}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Contact Me</DialogTitle>
|
||||
<DialogDescription asChild>
|
||||
<div className="flex flex-col gap-4">
|
||||
{emailContacts.map((emailContact) => (
|
||||
<div key={emailContact.address}>
|
||||
<p>{emailContact.label} : </p>
|
||||
<Item variant="outline" size="sm" asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copyEmail(emailContact.address)}
|
||||
onDoubleClick={() => openMailTo(emailContact.address)}
|
||||
className="active:scale-95 transition-transform"
|
||||
>
|
||||
<ItemMedia>
|
||||
<MailIcon className="size-5" />
|
||||
</ItemMedia>
|
||||
<ItemContent>
|
||||
<ItemTitle>{emailContact.address}</ItemTitle>
|
||||
</ItemContent>
|
||||
<ItemActions>
|
||||
{copiedEmail === emailContact.address ? (
|
||||
<Check className="size-4" />
|
||||
) : (
|
||||
<Copy className="size-4" />
|
||||
)}
|
||||
</ItemActions>
|
||||
</button>
|
||||
</Item>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter className="sm:justify-end">
|
||||
<DialogClose asChild>
|
||||
<Button type="button">Close</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue