201 lines
7.3 KiB
TypeScript
201 lines
7.3 KiB
TypeScript
"use client";
|
|
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { useState, useEffect } from "react";
|
|
import AnimatedButton from "./AnimatedButton";
|
|
|
|
const navLinks = [
|
|
{ href: "/", label: "Home" },
|
|
{ href: "/what-we-do", label: "What We Do" },
|
|
{ href: "/products", label: "Products" },
|
|
{ href: "/sustainability", label: "Sustainability" },
|
|
{ href: "/culture", label: "Our Culture" },
|
|
];
|
|
|
|
export default function Navigation() {
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 20);
|
|
};
|
|
|
|
window.addEventListener("scroll", handleScroll);
|
|
return () => window.removeEventListener("scroll", handleScroll);
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<motion.header
|
|
initial={{ y: -100 }}
|
|
animate={{ y: 0 }}
|
|
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
|
|
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-500 ${
|
|
isScrolled
|
|
? "bg-white/90 dark:bg-neutral-950/90 backdrop-blur-xl shadow-lg shadow-black/5"
|
|
: "bg-transparent"
|
|
}`}
|
|
>
|
|
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex items-center justify-between h-20">
|
|
{/* Logo */}
|
|
<Link href="/" className="relative z-10">
|
|
<motion.div
|
|
whileHover={{ scale: 1.05 }}
|
|
whileTap={{ scale: 0.95 }}
|
|
transition={{ type: "spring", stiffness: 400, damping: 17 }}
|
|
className="flex items-center gap-3"
|
|
>
|
|
<Image
|
|
src="/assets/brand-logo.png"
|
|
alt="TCM Sales & Marketing"
|
|
width={50}
|
|
height={50}
|
|
className="w-12 h-12 object-contain"
|
|
/>
|
|
<div className="hidden sm:block">
|
|
<span className="font-bold text-xl text-neutral-900 dark:text-white">
|
|
TCM
|
|
</span>
|
|
<span className="text-[hsl(0,100%,40%)] font-bold text-xl">
|
|
{" "}
|
|
Sales
|
|
</span>
|
|
</div>
|
|
</motion.div>
|
|
</Link>
|
|
|
|
{/* Desktop Navigation */}
|
|
<div className="hidden lg:flex items-center gap-1">
|
|
{navLinks.map((link) => (
|
|
<Link key={link.href} href={link.href}>
|
|
<motion.span
|
|
className="relative px-4 py-2 text-neutral-600 dark:text-neutral-300 font-medium rounded-full transition-colors hover:text-neutral-900 dark:hover:text-white cursor-pointer"
|
|
whileHover={{ scale: 1.05 }}
|
|
whileTap={{ scale: 0.95 }}
|
|
>
|
|
{link.label}
|
|
<motion.span
|
|
className="absolute bottom-0 left-1/2 -translate-x-1/2 w-1 h-1 bg-[hsl(0,100%,40%)] rounded-full opacity-0"
|
|
whileHover={{ opacity: 1, scale: 1.5 }}
|
|
/>
|
|
</motion.span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{/* CTA Button */}
|
|
<div className="hidden lg:block">
|
|
<AnimatedButton href="/contact" size="sm">
|
|
Contact Us
|
|
</AnimatedButton>
|
|
</div>
|
|
|
|
{/* Mobile Menu Button */}
|
|
<motion.button
|
|
whileHover={{ scale: 1.1 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
|
className="lg:hidden relative z-10 p-2 text-neutral-900 dark:text-white"
|
|
>
|
|
<div className="w-6 h-5 flex flex-col justify-between">
|
|
<motion.span
|
|
animate={{
|
|
rotate: isMobileMenuOpen ? 45 : 0,
|
|
y: isMobileMenuOpen ? 8 : 0,
|
|
}}
|
|
className="w-full h-0.5 bg-current origin-left transition-colors"
|
|
/>
|
|
<motion.span
|
|
animate={{
|
|
opacity: isMobileMenuOpen ? 0 : 1,
|
|
x: isMobileMenuOpen ? -20 : 0,
|
|
}}
|
|
className="w-full h-0.5 bg-current transition-colors"
|
|
/>
|
|
<motion.span
|
|
animate={{
|
|
rotate: isMobileMenuOpen ? -45 : 0,
|
|
y: isMobileMenuOpen ? -8 : 0,
|
|
}}
|
|
className="w-full h-0.5 bg-current origin-left transition-colors"
|
|
/>
|
|
</div>
|
|
</motion.button>
|
|
</div>
|
|
</nav>
|
|
</motion.header>
|
|
|
|
{/* Mobile Menu */}
|
|
<AnimatePresence>
|
|
{isMobileMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="fixed inset-0 z-40 lg:hidden"
|
|
>
|
|
{/* Backdrop */}
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
/>
|
|
|
|
{/* Menu Panel */}
|
|
<motion.div
|
|
initial={{ x: "100%" }}
|
|
animate={{ x: 0 }}
|
|
exit={{ x: "100%" }}
|
|
transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
|
className="absolute right-0 top-0 bottom-0 w-full max-w-sm bg-white dark:bg-neutral-950 shadow-2xl"
|
|
>
|
|
<div className="flex flex-col h-full pt-24 pb-8 px-6">
|
|
<nav className="flex-1 space-y-2">
|
|
{navLinks.map((link, index) => (
|
|
<motion.div
|
|
key={link.href}
|
|
initial={{ opacity: 0, x: 20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
transition={{ delay: index * 0.1 }}
|
|
>
|
|
<Link
|
|
href={link.href}
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
className="block py-4 text-2xl font-medium text-neutral-900 dark:text-white hover:text-[hsl(0,100%,40%)] transition-colors border-b border-neutral-100 dark:border-neutral-800"
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
</motion.div>
|
|
))}
|
|
</nav>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.5 }}
|
|
>
|
|
<AnimatedButton
|
|
href="/contact"
|
|
size="lg"
|
|
className="w-full justify-center"
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
>
|
|
Contact Us
|
|
</AnimatedButton>
|
|
</motion.div>
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</>
|
|
);
|
|
}
|