Skip to content

Instantly share code, notes, and snippets.

@martinadamsdev
Created November 18, 2024 20:18
Show Gist options
  • Save martinadamsdev/6394f96427666d82f8d32998d9786761 to your computer and use it in GitHub Desktop.
Save martinadamsdev/6394f96427666d82f8d32998d9786761 to your computer and use it in GitHub Desktop.
Navigation.jsx
import React, { useState, useEffect } from 'react';
import { Menu, X, ChevronDown, Sun, Moon } from 'lucide-react';
import {
Sheet,
SheetContent,
SheetTrigger,
} from "@/components/ui/sheet";
const Navigation = ({
theme = 'light',
onThemeChange = () => {},
companyName = 'Company',
navigationItems = [
{ label: 'Dashboard', href: '/dashboard' },
{ label: 'Projects', href: '/projects',
subItems: [
{ label: 'Active', href: '/projects/active' },
{ label: 'Archived', href: '/projects/archived' },
]
},
{ label: 'Team', href: '/team' },
{ label: 'Settings', href: '/settings' }
]
}) => {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [openDropdowns, setOpenDropdowns] = useState({});
const [isScrolled, setIsScrolled] = useState(false);
// Handle scroll effect for navbar
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 10);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const toggleDropdown = (index) => {
setOpenDropdowns(prev => ({
...prev,
[index]: !prev[index]
}));
};
const navClasses = `
fixed top-0 left-0 right-0 z-50 transition-all duration-300
${theme === 'dark'
? 'bg-gray-900 text-white'
: 'bg-white text-gray-800'}
${isScrolled
? 'shadow-lg'
: ''}
`;
const linkClasses = `
px-4 py-2 rounded-md transition-colors duration-200
${theme === 'dark'
? 'hover:bg-gray-800'
: 'hover:bg-gray-100'}
`;
const dropdownClasses = `
absolute top-full left-0 mt-1 w-48 rounded-md shadow-lg
${theme === 'dark'
? 'bg-gray-800 border border-gray-700'
: 'bg-white border border-gray-200'}
`;
const DesktopNav = () => (
<div className="hidden lg:flex items-center space-x-4">
{navigationItems.map((item, index) => (
<div key={index} className="relative group">
{item.subItems ? (
<div>
<button
className={`${linkClasses} flex items-center space-x-1`}
onClick={() => toggleDropdown(index)}
>
<span>{item.label}</span>
<ChevronDown size={16} />
</button>
{openDropdowns[index] && (
<div className={dropdownClasses}>
{item.subItems.map((subItem, subIndex) => (
<a
key={subIndex}
href={subItem.href}
className={`${linkClasses} block w-full`}
>
{subItem.label}
</a>
))}
</div>
)}
</div>
) : (
<a href={item.href} className={linkClasses}>
{item.label}
</a>
)}
</div>
))}
</div>
);
const MobileNav = () => (
<Sheet open={isMobileMenuOpen} onOpenChange={setIsMobileMenuOpen}>
<SheetTrigger asChild>
<button className="lg:hidden p-2">
<Menu size={24} />
</button>
</SheetTrigger>
<SheetContent side="left" className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white'}>
<nav className="flex flex-col space-y-4 mt-6">
{navigationItems.map((item, index) => (
<div key={index} className="flex flex-col">
{item.subItems ? (
<>
<button
className={`${linkClasses} flex items-center justify-between`}
onClick={() => toggleDropdown(index)}
>
{item.label}
<ChevronDown
size={16}
className={`transform transition-transform duration-200
${openDropdowns[index] ? 'rotate-180' : ''}`}
/>
</button>
{openDropdowns[index] && (
<div className="ml-4 space-y-2 mt-2">
{item.subItems.map((subItem, subIndex) => (
<a
key={subIndex}
href={subItem.href}
className={linkClasses}
>
{subItem.label}
</a>
))}
</div>
)}
</>
) : (
<a href={item.href} className={linkClasses}>
{item.label}
</a>
)}
</div>
))}
</nav>
</SheetContent>
</Sheet>
);
return (
<header className={navClasses}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo/Company Name */}
<div className="flex-shrink-0">
<a href="/" className="text-xl font-bold">
{companyName}
</a>
</div>
{/* Desktop Navigation */}
<DesktopNav />
{/* Theme Toggle & Mobile Menu */}
<div className="flex items-center space-x-4">
<button
onClick={() => onThemeChange(theme === 'dark' ? 'light' : 'dark')}
className={`p-2 rounded-md
${theme === 'dark'
? 'hover:bg-gray-800'
: 'hover:bg-gray-100'}`}
>
{theme === 'dark' ? <Sun size={20} /> : <Moon size={20} />}
</button>
<MobileNav />
</div>
</div>
</div>
</header>
);
};
export default Navigation;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment