Instantly share code, notes, and snippets.
Created
November 18, 2024 20:18
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save martinadamsdev/6394f96427666d82f8d32998d9786761 to your computer and use it in GitHub Desktop.
Navigation.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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