Skip to content
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

Merged
merged 3 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,15 @@
},
"engines": {
"node": ">=18.17.0"
},
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@react-three/fiber": "^8.17.7",
"@tabler/icons-react": "^3.16.0",
"@types/three": "^0.168.0",
"clsx": "^2.1.1",
"framer-motion": "^11.5.4",
"tailwind-merge": "^2.5.2",
"three": "^0.168.0"
}
}

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";
Copy link
Collaborator

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```.


import React from "react";
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to consider typing your page as NextPage since we are using TypeScript, as this ensures better integration and adds clarity for future maintainers by indicating that the component is a Next.js page.

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;
Loading