Skip to content

Commit

Permalink
refactor: improve site header with responsive hooks and hamburger menu
Browse files Browse the repository at this point in the history
- Extract hamburger menu into separate component
- Replace hidden classes with useBreakpoint hook
- Add menu/close icon toggle animation

References: shadcn-ui/ui#761 (comment)
  • Loading branch information
ligsnf committed Dec 26, 2024
1 parent 4a0f960 commit bcddbb8
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 59 deletions.
50 changes: 50 additions & 0 deletions src/components/hamburger-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Button } from "@/components/ui/button"
import { Menu, X } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Link } from '@tanstack/react-router'
import { useState } from 'react'

type MenuItem = {
name: string
to: string
}

interface HamburgerMenuProps {
items: MenuItem[]
}

export function HamburgerMenu({ items }: HamburgerMenuProps) {
const [isOpen, setIsOpen] = useState(false)

return (
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
{isOpen ? (
<X strokeWidth={3} />
) : (
<Menu />
)}
<span className="sr-only">Toggle navigation menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48">
{items.map((item) => (
<DropdownMenuItem key={item.to} asChild>
<Link
to={item.to}
className="w-full cursor-pointer font-medium text-muted-foreground [&.active]:text-foreground"
>
{item.name}
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}
96 changes: 37 additions & 59 deletions src/components/site-header.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { Link } from '@tanstack/react-router';
import { siteConfig } from '@/config/site';
import { ModeToggle } from '@/components/theme/mode-toggle';
import { HamburgerMenu } from '@/components/hamburger-menu';
import { Icons } from '@/components/icons';
import { ModeToggle } from '@/components/theme/mode-toggle';
import { Button } from '@/components/ui/button';
import { GraduationCap, Menu } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useBreakpoint } from '@/hooks/use-breakpoint';
import { GraduationCap } from 'lucide-react';

const navigationItems = [
{ name: 'Calculator', to: '/' },
Expand All @@ -29,74 +25,56 @@ function SiteLogo() {
}

export function SiteHeader() {
const isMobile = useBreakpoint('mobile')
const isDesktop = !isMobile

return (
<header className="sticky top-0 z-50 border-b bg-background">
<div className="mx-auto w-full max-w-4xl">
<div className="flex h-16 items-center justify-between px-4">
{/* Desktop Navigation */}
<nav className="hidden sm:flex items-center gap-8">
<SiteLogo />
<div className="flex gap-4 font-medium">
{navigationItems.map((item) => (
<Link
key={item.to}
to={item.to}
className="text-muted-foreground hover:text-foreground/70 [&.active]:text-foreground"
>
{item.name}
</Link>
))}
</div>
</nav>

{/* Mobile Navigation */}
<DropdownMenu>
<DropdownMenuTrigger asChild className="sm:hidden">
<Button variant="outline" size="icon">
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48">
{navigationItems.map((item) => (
<DropdownMenuItem key={item.to} asChild>
{isDesktop && (
<nav className="flex items-center gap-8">
<SiteLogo />
<div className="flex gap-4 font-medium">
{navigationItems.map((item) => (
<Link
key={item.to}
to={item.to}
className="w-full cursor-pointer font-medium text-muted-foreground [&.active]:text-foreground"
className="text-muted-foreground hover:text-foreground/70 [&.active]:text-foreground"
>
{item.name}
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
))}
</div>
</nav>
)}

{/* Mobile Logo (centered) */}
<div className="sm:hidden flex-1 flex justify-center">
<SiteLogo />
</div>
{/* Mobile Navigation */}
{isMobile && (
<>
<HamburgerMenu items={navigationItems} />
{/* Mobile Logo (centered) */}
<div className="flex-1 flex justify-center">
<SiteLogo />
</div>
</>
)}

{/* Right side items */}
<nav className="flex items-center gap-1">
<Button
asChild
variant="outline"
size="icon"
className="hidden sm:flex"
>
<a
href={siteConfig.links.github}
target="_blank"
rel="noreferrer"
>
<Icons.gitHub className="h-4 w-4" />
<span className="sr-only">GitHub</span>
</a>
</Button>
{isDesktop && (
<Button asChild variant="outline" size="icon">
<a href={siteConfig.links.github} target="_blank" rel="noreferrer">
<Icons.gitHub className="h-4 w-4" />
<span className="sr-only">GitHub</span>
</a>
</Button>
)}
<ModeToggle />
</nav>
</div>
</div>
</header>
);
)
}

0 comments on commit bcddbb8

Please sign in to comment.