feat: new smooth animation 🥰

This commit is contained in:
dvelo 2025-02-15 15:58:50 -06:00
parent 6f6e889719
commit 4f715cd9f2
2 changed files with 88 additions and 47 deletions

@ -32,11 +32,10 @@
import IconDisplay from "@/components/feat/icons/minecraft-icon-display"; import IconDisplay from "@/components/feat/icons/minecraft-icon-display";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import type { ServerResponse } from "@/lib/types/mh-server"; import type { ServerResponse } from "@/lib/types/mh-server";
import { Copy, ExternalLink, ServerCrash } from "lucide-react"; import { Check, Copy, ExternalLink, ServerCrash } from "lucide-react";
import { notFound, useSearchParams } from "next/navigation"; import { notFound, useSearchParams } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { CheckmarkIcon } from "react-hot-toast";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
import { import {
Tooltip, Tooltip,
@ -46,6 +45,7 @@ import {
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Link from "next/link"; import Link from "next/link";
import { AnimatePresence, motion } from "framer-motion";
export default function Embed({ params }: { params: { server: string } }) { export default function Embed({ params }: { params: { server: string } }) {
const [serverFound, setServerFound] = useState(true); const [serverFound, setServerFound] = useState(true);
@ -97,7 +97,14 @@ export default function Embed({ params }: { params: { server: string } }) {
</span> </span>
</Link> </Link>
<div className="px-4 pt-2 flex items-center group overflow-hidden"> <div className="px-4 pt-2 flex items-center group overflow-hidden">
<div className={staticMode ? "block" : "group-hover:block hidden"}> <div
className={cn(
staticMode ? "block" : "opacity-0 group-hover:opacity-100",
"transform transition-all duration-300 ease-in-out",
"group-hover:translate-x-0 -translate-x-full",
"absolute left-[10px] w-0 group-hover:w-[64px]"
)}
>
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<Button <Button
@ -110,7 +117,35 @@ export default function Embed({ params }: { params: { server: string } }) {
setTimeout(() => setCopied(false), 1000); setTimeout(() => setCopied(false), 1000);
}} }}
> >
{copied ? <CheckmarkIcon /> : <Copy size={16} />} <div className="relative w-full h-full grid place-items-center">
<AnimatePresence>
{copied ? (
<motion.div
key="check"
animate={{ opacity: 1, scale: 1 }}
initial={{ opacity: 0, scale: 0.3 }}
exit={{ opacity: 0, scale: 0.3 }}
transition={{ duration: 0.2, ease: "linear" }}
className="top-0 left-0"
style={{ gridRow: 1, gridColumn: 1 }}
>
<Check size={18} />
</motion.div>
) : (
<motion.div
key="clipboard"
animate={{ opacity: 1, scale: 1 }}
initial={{ opacity: 0, scale: 0.3 }}
exit={{ opacity: 0, scale: 0.3 }}
transition={{ duration: 0.2, ease: "linear" }}
className="top-0 left-0"
style={{ gridRow: 1, gridColumn: 1 }}
>
<Copy size={18} />
</motion.div>
)}
</AnimatePresence>
</div>
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Copy this server IP</TooltipContent> <TooltipContent>Copy this server IP</TooltipContent>
@ -126,49 +161,55 @@ export default function Embed({ params }: { params: { server: string } }) {
<ExternalLink size={16} /> <ExternalLink size={16} />
</Button> </Button>
</div> </div>
{serverObject && ( <div
<IconDisplay className={cn(
server={serverObject} "flex items-center transition-all duration-300 ease-in-out",
className={cn( staticMode ? "ml-0" : "group-hover:ml-[42px]"
"flex items-center mr-2",
staticMode ? "mb-1 ml-1" : "group-hover:mb-1 group-hover:ml-1"
)}
/>
)}
<div className={cn("block", staticMode ? "mb-1" : " group-hover:mb-1")}>
<strong className="text-lg flex items-center gap-2">
{params.server}
{!noShowBranding && <Badge variant="blue">on Minehut</Badge>}
</strong>{" "}
<span className="text-sm">Joined {serverObject?.joins} times</span>
{serverObject?.online && (
<span className="flex items-center">
{serverObject.playerCount === 0 ? (
<div
className="items-center border"
style={{
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
) : (
<div
className="items-center"
style={{
backgroundColor: "#0cce6b",
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
)}
<span className="text-sm ml-1">
{serverObject.playerCount} player(s) online
</span>
</span>
)} )}
>
{serverObject && (
<IconDisplay
server={serverObject}
className={cn(
"flex items-center mr-2",
staticMode ? "mb-1" : "group-hover:mb-1"
)}
/>
)}
<div className="block">
<strong className="text-lg flex items-center gap-2">
{params.server}
{!noShowBranding && <Badge variant="blue">on Minehut</Badge>}
</strong>
<span className="text-sm">Joined {serverObject?.joins} times</span>
{serverObject?.online && (
<span className="flex items-center">
{serverObject.playerCount === 0 ? (
<div
className="items-center border"
style={{
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
) : (
<div
className="items-center"
style={{
backgroundColor: "#0cce6b",
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
)}
<span className="text-sm ml-1">
{serverObject.playerCount} player(s) online
</span>
</span>
)}
</div>
</div> </div>
</div> </div>
</div> </div>

@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
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-shadcn-primary px-3 py-1.5 text-xs text-shadcn-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",
"border dark:border-slate-200 border-zinc-800 dark:border-b-slate-300 border-t-zinc-700", "border dark:border-slate-200 border-zinc-800 dark:border-b-slate-300 border-t-zinc-700",
className className
)} )}