mirror of
https://github.com/emmabostian/developer-portfolios.git
synced 2025-04-21 05:38:01 +00:00
190 lines
6.4 KiB
TypeScript
190 lines
6.4 KiB
TypeScript
|
"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;
|