Skip to content

Commit

Permalink
feat: add attr
Browse files Browse the repository at this point in the history
  • Loading branch information
listlessbird committed Nov 8, 2024
1 parent c81d9d5 commit 82d5311
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 5 deletions.
Binary file modified web/bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.3",
"@tanstack/react-query": "^5.56.2",
"@tanstack/react-query-devtools": "^5.58.0",
"ai": "^3.4.7",
Expand All @@ -47,6 +48,7 @@
"react": "19.0.0-rc-f994737d14-20240522",
"react-dom": "19.0.0-rc-f994737d14-20240522",
"react-hook-form": "^7.53.0",
"react-icons": "^5.3.0",
"replicate": "^0.34.1",
"tailwind-merge": "^2.5.3",
"together-ai": "^0.8.0",
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async function Layout({
<main className="min-h-screen flex-1">
<Header />
<HeaderMobile />
<div className="space-y-8 max-w-2xl mx-auto pt-5 px-3">
<div className="space-y-8 max-w-2xl mx-auto pt-5 px-3 lg:pl-16">
{children}
</div>
</main>
Expand Down
149 changes: 149 additions & 0 deletions web/src/components/attr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { useState, useRef, MouseEvent } from "react";
import { motion, useAnimation, AnimatePresence } from "framer-motion";
import { Github, Heart } from "lucide-react";
import {
TooltipProvider,
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
import { FaXTwitter } from "react-icons/fa6";

export function Attr() {
const controls = useAnimation();
const [isOpen, setIsOpen] = useState(false);
const timeoutRef = useRef(null);

const handleOpen = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
setIsOpen(true);
controls.start({
scale: [1, 1.2, 1],
transition: { duration: 0.5 },
});
};

const handleClose = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
// @ts-ignore
timeoutRef.current = setTimeout(() => {
setIsOpen(false);
}, 150);
};

const handleTooltipMouseEnter = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
setIsOpen(true);
};

const handleTooltipMouseLeave = () => {
handleClose();
};

const handleClick = () => {
setIsOpen(!isOpen);
};

const handleLinkClick = (e: MouseEvent<HTMLAnchorElement>) => {
e.stopPropagation();
setIsOpen(false);
};

return (
<TooltipProvider>
<Tooltip open={isOpen}>
<motion.div
className="p-4 bg-gradient-to-t from-background/80 to-transparent backdrop-blur-sm"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<motion.div
className="max-w-md mx-auto flex items-center justify-center lg:space-x-3 text-foreground"
onMouseEnter={handleOpen}
onMouseLeave={handleClose}
>
<span className="text-sm font-medium hidden lg:inline">
built with
</span>
<motion.div
animate={{
rotateY: [0, 360],
transition: { duration: 2, repeat: Infinity, ease: "linear" },
}}
>
<Heart className="size-5 text-red-500" fill="currentColor" />
</motion.div>
<motion.span
className="text-sm font-medium hidden lg:inline"
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.2 }}
>
by{" "}
<TooltipTrigger asChild>
<Button
variant="link"
className="p-0 h-auto font-medium underline-offset-4 hover:underline"
onClick={handleClick}
onFocus={handleOpen}
onBlur={handleClose}
>
listlessbird
</Button>
</TooltipTrigger>
</motion.span>
</motion.div>
</motion.div>
<AnimatePresence>
{isOpen && (
<TooltipContent
side="top"
sideOffset={5}
onMouseEnter={handleTooltipMouseEnter}
onMouseLeave={handleTooltipMouseLeave}
className="select-none cursor-default"
>
<motion.div
className="flex flex-col items-center space-y-2"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
>
<div className="flex items-center space-x-2">
<Github className="size-4" />
<a
href="https://github.com/listlessbird"
target="_blank"
rel="noopener noreferrer"
className="hover:underline"
onClick={handleLinkClick}
onFocus={handleOpen}
onBlur={handleClose}
>
github.com/listlessbird
</a>
</div>
<div className="flex items-center space-x-2">
<FaXTwitter className="size-4" />
<a
href="https://twitter.com/listlessbird"
target="_blank"
rel="noopener noreferrer"
className="hover:underline"
onClick={handleLinkClick}
onFocus={handleOpen}
onBlur={handleClose}
>
@listlessbird
</a>
</div>
</motion.div>
</TooltipContent>
)}
</AnimatePresence>
</Tooltip>
</TooltipProvider>
);
}
2 changes: 1 addition & 1 deletion web/src/components/nav/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const SideNavItems: SideNavItem[] = [
icon: <HomeIcon />,
},
{
title: "generations",
title: "Generations",
path: "/history",
icon: <VideoIcon />,
},
Expand Down
6 changes: 5 additions & 1 deletion web/src/components/nav/header-mobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ const MenuItem = ({
children?: ReactNode;
} & ComponentProps<typeof motion.li>) => {
return (
<motion.li variants={MenuItemVariants} className={className} {...props}>
<motion.li
variants={MenuItemVariants}
className={cn("capitalize", className)}
{...props}
>
{children}
</motion.li>
);
Expand Down
9 changes: 7 additions & 2 deletions web/src/components/nav/sidenav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { usePathname } from "next/navigation";
import { SideNavItems, SideNavItem } from "@/components/nav/constants";
import { cn } from "@/lib/utils";
import { Logo } from "@/components/logo";
import { Attr } from "@/components/attr";
export function SideNav() {
return (
<div
className="md:w-16 lg:w-60 bg-background h-screen flex-1 fixed border-r border-zinc-200 hidden md:flex shadow-md"
className="md:w-16 lg:w-60 bg-background h-screen flex-1 fixed border-r border-zinc-200 hidden md:flex shadow-md md:flex-col"
sp-layout="sidebar"
>
<div className="flex flex-col space-y-6 w-full">
Expand All @@ -26,6 +27,10 @@ export function SideNav() {
})}
</div>
</div>

<div className="mt-auto">
<Attr />
</div>
</div>
);
}
Expand All @@ -38,7 +43,7 @@ const MenuItem = ({ item }: { item: SideNavItem }) => {
<Link
href={item.path}
className={cn(
`flex flex-row space-x-4 items-center p-2 rounded-lg hover:bg-accent`,
`flex flex-row space-x-4 items-center p-2 rounded-lg hover:bg-accent capitalize`,
item.path === pathname && "bg-zinc-100"
)}
title={item.title}
Expand Down
30 changes: 30 additions & 0 deletions web/src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client"

import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"

import { cn } from "@/lib/utils"

const TooltipProvider = TooltipPrimitive.Provider

const Tooltip = TooltipPrimitive.Root

const TooltipTrigger = TooltipPrimitive.Trigger

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md 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
)}
{...props}
/>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

0 comments on commit 82d5311

Please sign in to comment.