mirror of
https://github.com/guezoloic/website.git
synced 2026-03-28 18:03:50 +00:00
feat: rework entire project structure
This commit is contained in:
112
app/components/sections/Navbar.tsx
Normal file
112
app/components/sections/Navbar.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import React, { Dispatch, SetStateAction } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { motion, AnimatePresence, Variants } from "framer-motion";
|
||||
import * as SOLID from "@heroicons/react/24/solid";
|
||||
|
||||
import Button from "@app/components/ui/Button";
|
||||
import content from "@app/data/content.json";
|
||||
import { MenuState } from "@/app/page";
|
||||
import Lang from "@app/components/ui/LangSwitcher"
|
||||
|
||||
type NavbarProps = {
|
||||
state: MenuState;
|
||||
setState: Dispatch<SetStateAction<MenuState>>;
|
||||
isOpen: boolean;
|
||||
};
|
||||
|
||||
const navVariants: Variants = {
|
||||
initial: { scaleY: 0.8, scaleX: 0.1, opacity: 0.7 },
|
||||
animate: { scaleY: 1, scaleX: 1, opacity: 1, transition: { duration: 0.3, ease: [0.42, 0, 0.58, 1] } },
|
||||
};
|
||||
|
||||
const exitVariants: Variants = {
|
||||
initial: { scaleY: 1, scaleX: 5, opacity: 0.7 },
|
||||
animate: { scaleY: 1, scaleX: 1, opacity: 1, transition: { duration: 0.3, ease: [0.42, 0.66, 0.58, 1] } },
|
||||
};
|
||||
|
||||
|
||||
export default function Navbar({ state, setState, isOpen }: NavbarProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const handleClick = (key: keyof MenuState) => {
|
||||
setState(prev => ({ ...prev, [key]: true }));
|
||||
};
|
||||
|
||||
const closeWindows = () => {
|
||||
setState(prev => {
|
||||
const newState: MenuState = {} as MenuState;
|
||||
for (const key in prev) newState[key as keyof MenuState] = false;
|
||||
return newState;
|
||||
});
|
||||
};
|
||||
|
||||
const mainButton = content.navbar.buttons[0];
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen ? (
|
||||
<motion.div
|
||||
key="close"
|
||||
className="fixed bottom-4 left-1/2 transform -translate-x-1/2 flex justify-center z-50"
|
||||
variants={exitVariants}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
exit="exit"
|
||||
>
|
||||
<Button
|
||||
onClick={closeWindows}
|
||||
label="exit"
|
||||
variant="icon"
|
||||
>
|
||||
<SOLID.XMarkIcon className="w-8 h-8 text-white" />
|
||||
</Button>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="menu"
|
||||
className="fixed bottom-4 left-1/2 transform -translate-x-1/2 flex items-center gap-2 z-50"
|
||||
variants={navVariants}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
exit="exit"
|
||||
>
|
||||
<Button
|
||||
onClick={() => handleClick(mainButton.action as keyof MenuState)}
|
||||
label={t(mainButton.label)}
|
||||
variant="text"
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center whitespace-nowrap">
|
||||
<span className="text-base md:text-lg font-bold text-white drop-shadow-lg">
|
||||
{content.name}
|
||||
</span>
|
||||
<span className="text-xs md:text-sm text-gray-300 font-light">
|
||||
{t(content.career)}
|
||||
</span>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{content.navbar.buttons.slice(1).map((btn, i) => {
|
||||
const Icon = (SOLID as Record<string, React.ElementType>)[btn.icon];
|
||||
return (
|
||||
<div className="relative group" key={i}>
|
||||
<Button
|
||||
onClick={() => handleClick(btn.action as keyof MenuState)}
|
||||
label={t(btn.label)}
|
||||
variant="icon"
|
||||
>
|
||||
{Icon && <Icon className="w-6 h-6 text-white" />}
|
||||
</Button>
|
||||
<span className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 rounded-md text-xs text-white bg-black/80 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
|
||||
{t(btn.label)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
);
|
||||
})}
|
||||
<Lang />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user