-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #2 - created my personal page ~ STYLES (Kartik) #27
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/** | ||
* Note: Use position fixed according to your needs | ||
* Desktop navbar is better positioned at the bottom | ||
* Mobile navbar is better positioned at bottom right. | ||
**/ | ||
import { useRef, useState } from "react"; | ||
import Link from "next/link"; | ||
import { cn } from "../_lib/utils"; | ||
import { IconLayoutNavbarCollapse } from "@tabler/icons-react"; | ||
import { AnimatePresence, MotionValue, motion, useMotionValue, useSpring, useTransform } from "framer-motion"; | ||
|
||
export const FloatingDock = ({ | ||
items, | ||
desktopClassName, | ||
mobileClassName, | ||
}: { | ||
items: { title: string; icon: React.ReactNode; href: string }[]; | ||
desktopClassName?: string; | ||
mobileClassName?: string; | ||
}) => { | ||
return ( | ||
<> | ||
<FloatingDockDesktop items={items} className={desktopClassName} /> | ||
<FloatingDockMobile items={items} className={mobileClassName} /> | ||
</> | ||
); | ||
}; | ||
|
||
const FloatingDockMobile = ({ | ||
items, | ||
className, | ||
}: { | ||
items: { title: string; icon: React.ReactNode; href: string }[]; | ||
className?: string; | ||
}) => { | ||
const [open, setOpen] = useState(false); | ||
return ( | ||
<div className={cn("relative block md:hidden", className)}> | ||
<AnimatePresence> | ||
{open && ( | ||
<motion.div layoutId="nav" className="absolute bottom-full mb-2 inset-x-0 flex flex-col gap-2"> | ||
{items.map((item, idx) => ( | ||
<motion.div | ||
key={item.title} | ||
initial={{ opacity: 0, y: 10 }} | ||
animate={{ | ||
opacity: 1, | ||
y: 0, | ||
}} | ||
exit={{ | ||
opacity: 0, | ||
y: 10, | ||
transition: { | ||
delay: idx * 0.05, | ||
}, | ||
}} | ||
transition={{ delay: (items.length - 1 - idx) * 0.05 }} | ||
> | ||
<Link | ||
href={item.href} | ||
key={item.title} | ||
target="_blank" | ||
className="h-10 w-10 rounded-full bg-gray-50 dark:bg-neutral-900 flex items-center justify-center" | ||
> | ||
<div className="h-4 w-4">{item.icon}</div> | ||
</Link> | ||
</motion.div> | ||
))} | ||
</motion.div> | ||
)} | ||
</AnimatePresence> | ||
<button | ||
onClick={() => setOpen(!open)} | ||
className="h-10 w-10 rounded-full bg-gray-50 dark:bg-neutral-800 flex items-center justify-center" | ||
> | ||
<IconLayoutNavbarCollapse className="h-5 w-5 text-neutral-500 dark:text-neutral-400" /> | ||
</button> | ||
</div> | ||
); | ||
}; | ||
|
||
const FloatingDockDesktop = ({ | ||
items, | ||
className, | ||
}: { | ||
items: { title: string; icon: React.ReactNode; href: string }[]; | ||
className?: string; | ||
}) => { | ||
const mouseX = useMotionValue(Infinity); | ||
return ( | ||
<motion.div | ||
onMouseMove={e => mouseX.set(e.pageX)} | ||
onMouseLeave={() => mouseX.set(Infinity)} | ||
className={cn( | ||
"mx-auto hidden md:flex h-16 gap-4 items-end rounded-2xl bg-gray-50 dark:bg-neutral-900 px-4 pb-3", | ||
className, | ||
)} | ||
> | ||
{items.map(item => ( | ||
<IconContainer mouseX={mouseX} key={item.title} {...item} /> | ||
))} | ||
</motion.div> | ||
); | ||
}; | ||
|
||
function IconContainer({ | ||
mouseX, | ||
title, | ||
icon, | ||
href, | ||
}: { | ||
mouseX: MotionValue; | ||
title: string; | ||
icon: React.ReactNode; | ||
href: string; | ||
}) { | ||
const ref = useRef<HTMLDivElement>(null); | ||
|
||
const distance = useTransform(mouseX, val => { | ||
const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }; | ||
|
||
return val - bounds.x - bounds.width / 2; | ||
}); | ||
|
||
const widthTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]); | ||
const heightTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]); | ||
|
||
const widthTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]); | ||
const heightTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]); | ||
|
||
const width = useSpring(widthTransform, { | ||
mass: 0.1, | ||
stiffness: 150, | ||
damping: 12, | ||
}); | ||
const height = useSpring(heightTransform, { | ||
mass: 0.1, | ||
stiffness: 150, | ||
damping: 12, | ||
}); | ||
|
||
const widthIcon = useSpring(widthTransformIcon, { | ||
mass: 0.1, | ||
stiffness: 150, | ||
damping: 12, | ||
}); | ||
const heightIcon = useSpring(heightTransformIcon, { | ||
mass: 0.1, | ||
stiffness: 150, | ||
damping: 12, | ||
}); | ||
|
||
const [hovered, setHovered] = useState(false); | ||
|
||
return ( | ||
<Link href={href} target="_blank"> | ||
<motion.div | ||
ref={ref} | ||
style={{ width, height }} | ||
onMouseEnter={() => setHovered(true)} | ||
onMouseLeave={() => setHovered(false)} | ||
className="aspect-square rounded-full bg-gray-200 dark:bg-neutral-800 flex items-center justify-center relative" | ||
> | ||
<AnimatePresence> | ||
{hovered && ( | ||
<motion.div | ||
initial={{ opacity: 0, y: 10, x: "-50%" }} | ||
animate={{ opacity: 1, y: 0, x: "-50%" }} | ||
exit={{ opacity: 0, y: 2, x: "-50%" }} | ||
className="px-2 py-0.5 whitespace-pre rounded-md bg-gray-100 border dark:bg-neutral-800 dark:border-neutral-900 dark:text-white border-gray-200 text-neutral-700 absolute left-1/2 -translate-x-1/2 -top-8 w-fit text-xs" | ||
> | ||
{title} | ||
</motion.div> | ||
)} | ||
</AnimatePresence> | ||
<motion.div style={{ width: widthIcon, height: heightIcon }} className="flex items-center justify-center"> | ||
{icon} | ||
</motion.div> | ||
</motion.div> | ||
</Link> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { ClassValue, clsx } from "clsx"; | ||
import { twMerge } from "tailwind-merge"; | ||
|
||
export function cn(...inputs: ClassValue[]) { | ||
return twMerge(clsx(inputs)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
"use client"; | ||
|
||
import React from "react"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to explicitly import React in Next.js anymore, starting from Next.js 12.2 and React 17 and above. With these versions, Next.js automatically imports React when needed, so you can safely omit import React from "react" at the top of your files. |
||
import { BackgroundLines } from "./_components/bg-lines"; | ||
import { FloatingDock } from "./_components/floating_bar"; | ||
import { | ||
IconBrandGithub, | ||
IconBrandInstagram, | ||
IconBrandTelegram, | ||
IconBrandX, | ||
IconHome, | ||
IconTower, | ||
} from "@tabler/icons-react"; | ||
import { Address } from "~~/components/scaffold-eth"; | ||
|
||
const links = [ | ||
{ | ||
title: "Website", | ||
icon: <IconHome className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://styles-portfolio.vercel.app/", | ||
}, | ||
{ | ||
title: "GitHub", | ||
icon: <IconBrandGithub className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://github.com/ishtails", | ||
}, | ||
{ | ||
title: "BuidlGuidl", | ||
icon: <IconTower className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://app.buidlguidl.com/builders/0x5D56b71abE6cA1Dc208Ed85926178f9758fa879c", | ||
}, | ||
{ | ||
title: "Telegram", | ||
icon: <IconBrandTelegram className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://t.me/ishtails", | ||
}, | ||
{ | ||
title: "X", | ||
icon: <IconBrandX className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://x.com/ishtails", | ||
}, | ||
{ | ||
title: "Instagram", | ||
icon: <IconBrandInstagram className="h-full w-full text-neutral-500 dark:text-neutral-300" />, | ||
href: "https://www.instagram.com/ishtails", | ||
}, | ||
]; | ||
|
||
const STYLESProfilePage = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might want to consider typing your page as |
||
return ( | ||
<div> | ||
<div className="dark:bg-zinc-900 bg-zinc-100 text-zinc-100"> | ||
<BackgroundLines className="flex items-center min-h-screen justify-center w-full flex-col px-4"> | ||
<h2 className="bg-clip-text text-transparent text-center bg-gradient-to-b from-zinc-900 to-zinc-700 dark:from-zinc-600 dark:to-white text-[5rem] md:text-[10rem] lg:text-[15rem] xl:text-[20rem] font-sans py-2 md:py-10 relative z-20 tracking-widest"> | ||
STYLES. | ||
</h2> | ||
|
||
<p className="mx-auto text-sm italic md:text-lg text-zinc-600 dark:text-zinc-300 text-center"> | ||
full stack developer, designer & achitect, web3 enthusiast, musician, and possibly a human. | ||
</p> | ||
|
||
<FloatingDock items={links} /> | ||
|
||
<div className="text-zinc-800 my-6 dark:text-zinc-100"> | ||
<Address address="0x5D56b71abE6cA1Dc208Ed85926178f9758fa879c" /> | ||
</div> | ||
</BackgroundLines> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default STYLESProfilePage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to remove
"use client;" here and apply it directly to
floating_bar.tsx```.