'use client';import * as React from 'react';import { useEffect, useState, useRef, useId } from 'react';import { HashIcon, HouseIcon, MailIcon, SearchIcon, UsersRound, BellIcon, UserIcon, ChevronDownIcon,} from 'lucide-react';import { Button } from '@/components/ui/button';import { Input } from '@/components/ui/input';import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, NavigationMenuList,} from '@/components/ui/navigation-menu';import { Popover, PopoverContent, PopoverTrigger,} from '@/components/ui/popover';import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,} from '@/components/ui/dropdown-menu';import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';import { Badge } from '@/components/ui/badge';import { cn } from '@/lib/utils';import type { ComponentProps } from 'react';// Simple logo component for the navbarconst Logo = (props: React.SVGAttributes<SVGElement>) => { return ( <svg width='1em' height='1em' viewBox='0 0 324 323' fill='currentColor' xmlns='http://www.w3.org/2000/svg' {...(props as any)}> <rect x='88.1023' y='144.792' width='151.802' height='36.5788' rx='18.2894' transform='rotate(-38.5799 88.1023 144.792)' fill='currentColor' /> <rect x='85.3459' y='244.537' width='151.802' height='36.5788' rx='18.2894' transform='rotate(-38.5799 85.3459 244.537)' fill='currentColor' /> </svg> );};// Hamburger icon componentconst HamburgerIcon = ({ className, ...props }: React.SVGAttributes<SVGElement>) => ( <svg className={cn('pointer-events-none', className)} width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" xmlns="http://www.w3.org/2000/svg" {...(props as any)} > <path d="M4 12L20 12" className="origin-center -translate-y-[7px] transition-all duration-300 ease-[cubic-bezier(.5,.85,.25,1.1)] group-aria-expanded:translate-x-0 group-aria-expanded:translate-y-0 group-aria-expanded:rotate-[315deg]" /> <path d="M4 12H20" className="origin-center transition-all duration-300 ease-[cubic-bezier(.5,.85,.25,1.8)] group-aria-expanded:rotate-45" /> <path d="M4 12H20" className="origin-center translate-y-[7px] transition-all duration-300 ease-[cubic-bezier(.5,.85,.25,1.1)] group-aria-expanded:translate-y-0 group-aria-expanded:rotate-[135deg]" /> </svg>);// Notification Menu Componentconst NotificationMenu = ({ notificationCount = 3, onItemClick }: { notificationCount?: number; onItemClick?: (item: string) => void;}) => ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" size="icon" className="h-8 w-8 relative rounded-full"> <BellIcon size={16} /> {notificationCount > 0 && ( <Badge className="absolute -top-1 -right-1 h-5 w-5 flex items-center justify-center p-0 text-xs"> {notificationCount > 9 ? '9+' : notificationCount} </Badge> )} <span className="sr-only">Notifications</span> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-80"> <DropdownMenuLabel>Notifications</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem onClick={() => onItemClick?.('notification1')}> <div className="flex flex-col gap-1"> <p className="text-sm font-medium">New message received</p> <p className="text-xs text-muted-foreground">2 minutes ago</p> </div> </DropdownMenuItem> <DropdownMenuItem onClick={() => onItemClick?.('notification2')}> <div className="flex flex-col gap-1"> <p className="text-sm font-medium">System update available</p> <p className="text-xs text-muted-foreground">1 hour ago</p> </div> </DropdownMenuItem> <DropdownMenuItem onClick={() => onItemClick?.('notification3')}> <div className="flex flex-col gap-1"> <p className="text-sm font-medium">Weekly report ready</p> <p className="text-xs text-muted-foreground">3 hours ago</p> </div> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem onClick={() => onItemClick?.('view-all')}> View all notifications </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>);// User Menu Componentconst UserMenu = ({ userName = 'John Doe', userEmail = '[email protected]', userAvatar, onItemClick}: { userName?: string; userEmail?: string; userAvatar?: string; onItemClick?: (item: string) => void;}) => ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" className="h-9 px-2 py-0 hover:bg-accent hover:text-accent-foreground"> <Avatar className="h-7 w-7"> <AvatarImage src={userAvatar} alt={userName} /> <AvatarFallback className="text-xs"> {userName.split(' ').map(n => n[0]).join('')} </AvatarFallback> </Avatar> <ChevronDownIcon className="h-3 w-3 ml-1" /> <span className="sr-only">User menu</span> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-56"> <DropdownMenuLabel> <div className="flex flex-col space-y-1"> <p className="text-sm font-medium leading-none">{userName}</p> <p className="text-xs leading-none text-muted-foreground"> {userEmail} </p> </div> </DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem onClick={() => onItemClick?.('profile')}> Profile </DropdownMenuItem> <DropdownMenuItem onClick={() => onItemClick?.('settings')}> Settings </DropdownMenuItem> <DropdownMenuItem onClick={() => onItemClick?.('billing')}> Billing </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem onClick={() => onItemClick?.('logout')}> Log out </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu>);// Typesexport interface Navbar09NavItem { href?: string; label: string; icon: React.ComponentType<{ size?: number; className?: string; 'aria-hidden'?: boolean }>;}export interface Navbar09Props extends React.HTMLAttributes<HTMLElement> { logo?: React.ReactNode; logoHref?: string; navigationLinks?: Navbar09NavItem[]; searchPlaceholder?: string; userName?: string; userEmail?: string; userAvatar?: string; notificationCount?: number; messageIndicator?: boolean; onNavItemClick?: (href: string) => void; onSearchSubmit?: (query: string) => void; onMessageClick?: () => void; onNotificationItemClick?: (item: string) => void; onUserItemClick?: (item: string) => void;}// Default navigation links with iconsconst defaultNavigationLinks: Navbar09NavItem[] = [ { href: '#', label: 'Home', icon: HouseIcon }, { href: '#', label: 'Hash', icon: HashIcon }, { href: '#', label: 'Groups', icon: UsersRound },];export const Navbar09 = React.forwardRef<HTMLElement, Navbar09Props>( ( { className, logo = <Logo />, logoHref = '#', navigationLinks = defaultNavigationLinks, searchPlaceholder = 'Search...', userName = 'John Doe', userEmail = '[email protected]', userAvatar, notificationCount = 3, messageIndicator = true, onNavItemClick, onSearchSubmit, onMessageClick, onNotificationItemClick, onUserItemClick, ...props }, ref ) => { const [isMobile, setIsMobile] = useState(false); const containerRef = useRef<HTMLElement>(null); const searchId = useId(); useEffect(() => { const checkWidth = () => { if (containerRef.current) { const width = containerRef.current.offsetWidth; setIsMobile(width < 768); // 768px is md breakpoint } }; checkWidth(); const resizeObserver = new ResizeObserver(checkWidth); if (containerRef.current) { resizeObserver.observe(containerRef.current); } return () => { resizeObserver.disconnect(); }; }, []); // Combine refs const combinedRef = React.useCallback((node: HTMLElement | null) => { containerRef.current = node; if (typeof ref === 'function') { ref(node); } else if (ref) { ref.current = node; } }, [ref]); const handleSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); const formData = new FormData(e.currentTarget); const query = formData.get('search') as string; if (onSearchSubmit) { onSearchSubmit(query); } }; return ( <header ref={combinedRef} className={cn( 'sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 px-4 md:px-6 [&_*]:no-underline', className )} {...(props as any)} > <div className="container mx-auto flex h-16 max-w-screen-2xl items-center justify-between gap-4"> {/* Left side */} <div className="flex flex-1 items-center gap-2"> {/* Mobile menu trigger */} {isMobile && ( <Popover> <PopoverTrigger asChild> <Button className="group h-8 w-8 hover:bg-accent hover:text-accent-foreground" variant="ghost" size="icon" > <HamburgerIcon /> </Button> </PopoverTrigger> <PopoverContent align="start" className="w-48 p-1"> <NavigationMenu className="max-w-none"> <NavigationMenuList className="flex-col items-start gap-0"> {navigationLinks.map((link, index) => { const Icon = link.icon; return ( <NavigationMenuItem key={index} className="w-full"> <button onClick={(e) => { e.preventDefault(); if (onNavItemClick && link.href) onNavItemClick(link.href); }} className="flex w-full items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground cursor-pointer no-underline" > <Icon size={16} className="text-muted-foreground" aria-hidden={true} /> <span>{link.label}</span> </button> </NavigationMenuItem> ); })} </NavigationMenuList> </NavigationMenu> </PopoverContent> </Popover> )} <div className="flex items-center gap-6"> <button onClick={(e) => e.preventDefault()} className="flex items-center space-x-2 text-primary hover:text-primary/90 transition-colors cursor-pointer" > <div className="text-2xl"> {logo} </div> <span className="hidden font-bold text-xl sm:inline-block">shadcn.io</span> </button> {/* Search form */} <form onSubmit={handleSearchSubmit} className="relative"> <Input id={searchId} name="search" className="peer h-8 ps-8 pe-2" placeholder={searchPlaceholder} type="search" /> <div className="text-muted-foreground/80 pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-2 peer-disabled:opacity-50"> <SearchIcon size={16} /> </div> </form> </div> </div> {/* Middle area */} {!isMobile && ( <NavigationMenu className="flex"> <NavigationMenuList className="gap-2"> {navigationLinks.map((link, index) => { const Icon = link.icon; return ( <NavigationMenuItem key={index}> <NavigationMenuLink href={link.href} onClick={(e) => { e.preventDefault(); if (onNavItemClick && link.href) onNavItemClick(link.href); }} className="flex size-8 items-center justify-center p-1.5 rounded-md transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer" title={link.label} > <Icon aria-hidden={true} /> <span className="sr-only">{link.label}</span> </NavigationMenuLink> </NavigationMenuItem> ); })} </NavigationMenuList> </NavigationMenu> )} {/* Right side */} <div className="flex flex-1 items-center justify-end gap-4"> <div className="flex items-center gap-2"> {/* Messages */} <Button size="icon" variant="ghost" className="text-muted-foreground relative size-8 rounded-full shadow-none" aria-label="Open messages" onClick={(e) => { e.preventDefault(); if (onMessageClick) onMessageClick(); }} > <MailIcon size={16} aria-hidden={true} /> {messageIndicator && ( <div aria-hidden={true} className="bg-primary absolute top-0.5 right-0.5 size-1 rounded-full" /> )} </Button> {/* Notification menu */} <NotificationMenu notificationCount={notificationCount} onItemClick={onNotificationItemClick} /> </div> {/* User menu */} <UserMenu userName={userName} userEmail={userEmail} userAvatar={userAvatar} onItemClick={onUserItemClick} /> </div> </div> </header> ); });Navbar09.displayName = 'Navbar09';export { Logo, HamburgerIcon, NotificationMenu, UserMenu };