developer-portfolios/app/components/NavigationBar.tsx

190 lines
6.4 KiB
TypeScript
Raw Normal View History

2025-04-11 01:05:41 +00:00
"use client";
import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { useTheme } from "../Contexts/ThemeContext";
import { SunIcon, MoonIcon, Menu, X } from "lucide-react";
interface NavigationBarProps {
activeSection: string;
}
const NavigationBar: React.FC<NavigationBarProps> = ({ activeSection }) => {
const { theme, toggleTheme } = useTheme();
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const navItems = [
{ id: "home", label: "Home" },
{ id: "about", label: "About" },
{ id: "skills", label: "Skills" },
{ id: "experience", label: "Experience" },
{ id: "projects", label: "Projects" },
{ id: "education", label: "Education" },
{ id: "contact", label: "Contact" },
];
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 10);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const scrollToSection = (sectionId: string) => {
setIsMobileMenuOpen(false);
const element = document.getElementById(sectionId);
if (element) {
window.scrollTo({
top: element.offsetTop - 80,
behavior: "smooth",
});
}
};
return (
<motion.nav
className={`fixed top-0 left-0 right-0 z-50 px-4 sm:px-6 md:px-12 transition-all duration-300 ${
isScrolled
? "bg-white/80 dark:bg-background-dark/80 shadow-md backdrop-blur-md"
: "bg-transparent"
}`}
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ duration: 0.5 }}
>
<div className="container mx-auto flex justify-between items-center">
<motion.a
href="#home"
className="text-2xl font-bold flex items-center gap-2"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={(e) => {
e.preventDefault();
scrollToSection("home");
}}
>
<div className="flex items-center">
<img
src="./ucefLogo.png"
alt="Logo"
className="h-14 w-14"
/>
<span className="gradient-text bg-clip-text text-transparent bg-gradient-to-r from-primary to-secondary">
Youssef
</span>
</div>
</motion.a>
{/* Desktop Menu */}
<div className="hidden md:flex items-center space-x-8">
{navItems.map((item) => (
<motion.a
key={item.id}
href={`#${item.id}`}
className={`text-sm font-medium transition-colors duration-300 hover:text-primary relative ${
activeSection === item.id
? "text-primary dark:text-primary-light"
: "text-gray-600 dark:text-gray-300"
}`}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={(e) => {
e.preventDefault();
scrollToSection(item.id);
}}
>
{item.label}
{activeSection === item.id && (
<motion.span
className="absolute -bottom-1 left-0 right-0 h-0.5 bg-primary dark:bg-primary-light"
layoutId="navIndicator"
transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
)}
</motion.a>
))}
<motion.button
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
className="p-2 rounded-full bg-surface-light dark:bg-surface-dark hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-300"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={toggleTheme}
>
{theme === "dark" ? (
<SunIcon className="h-5 w-5 text-yellow-400" />
) : (
<MoonIcon className="h-5 w-5 text-gray-600" />
)}
</motion.button>
</div>
{/* Mobile Menu Button */}
<div className="flex items-center space-x-4 md:hidden">
<motion.button
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
className="p-2 rounded-full bg-surface-light dark:bg-surface-dark hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-300"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={toggleTheme}
>
{theme === "dark" ? (
<SunIcon className="h-5 w-5 text-yellow-400" />
) : (
<MoonIcon className="h-5 w-5 text-gray-600" />
)}
</motion.button>
<motion.button
className="p-2 rounded-md hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-300"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? (
<X className="h-6 w-6" />
) : (
<Menu className="h-6 w-6" />
)}
</motion.button>
</div>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<motion.div
className="absolute top-full left-0 right-0 bg-white dark:bg-background-dark shadow-lg rounded-b-lg py-4 md:hidden"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.2 }}
>
<div className="flex flex-col space-y-4 px-6">
{navItems.map((item) => (
<a
key={item.id}
href={`#${item.id}`}
className={`py-2 text-base font-medium transition-colors duration-300 hover:text-primary ${
activeSection === item.id
? "text-primary dark:text-primary-light"
: "text-gray-600 dark:text-gray-300"
}`}
onClick={(e) => {
e.preventDefault();
scrollToSection(item.id);
}}
>
{item.label}
</a>
))}
</div>
</motion.div>
)}
</motion.nav>
);
};
export default NavigationBar;