/** * Topbar Component - Horizontal Navigation Bar * * A top navigation bar with: * - Logo/brand area * - Horizontal nav links * - Search (optional) * - User menu * - Notification bell */ import React, { useState, useRef, useEffect, type ReactNode } from 'react' // ============================================================================ // Types // ============================================================================ export interface NavItem { id: string label: string path?: string icon?: ReactNode badge?: number | string onClick?: () => void } interface TopbarProps { /** Logo or brand element */ logo?: ReactNode /** Navigation items */ navItems?: NavItem[] /** Currently active item ID */ activeId?: string /** Callback when nav item clicked */ onNavigate?: (item: NavItem) => void /** Show search input */ showSearch?: boolean /** Search placeholder */ searchPlaceholder?: string /** Search callback */ onSearch?: (query: string) => void /** Notification count */ notificationCount?: number /** Notification click handler */ onNotificationClick?: () => void /** User info */ user?: { name?: string email?: string imageUrl?: string } /** User menu items */ userMenuItems?: Array<{ id: string label: string icon?: ReactNode onClick?: () => void danger?: boolean }> /** Custom right-side content */ rightContent?: ReactNode /** Sticky positioning */ sticky?: boolean } // ============================================================================ // Icons // ============================================================================ function SearchIcon({ className = '' }: { className?: string }) { return ( ) } function BellIcon({ className = '' }: { className?: string }) { return ( ) } function ChevronDownIcon({ className = '' }: { className?: string }) { return ( ) } // ============================================================================ // Dropdown Menu // ============================================================================ interface DropdownProps { trigger: ReactNode children: ReactNode align?: 'left' | 'right' } function Dropdown({ trigger, children, align = 'right' }: DropdownProps) { const [isOpen, setIsOpen] = useState(false) const ref = useRef(null) useEffect(() => { function handleClickOutside(event: MouseEvent) { if (ref.current && !ref.current.contains(event.target as Node)) { setIsOpen(false) } } document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, []) return (
setIsOpen(!isOpen)}>{trigger}
{isOpen && (
{children}
)}
) } // ============================================================================ // Topbar Component // ============================================================================ export default function Topbar({ logo, navItems = [], activeId, onNavigate, showSearch = false, searchPlaceholder = 'Search...', onSearch, notificationCount, onNotificationClick, user, userMenuItems = [], rightContent, sticky = true, }: TopbarProps) { const [searchQuery, setSearchQuery] = useState('') const handleSearch = (e: React.FormEvent) => { e.preventDefault() onSearch?.(searchQuery) } return (
{/* Left: Logo + Nav */}
{logo && (
{logo}
)} {navItems.length > 0 && ( )}
{/* Right: Search, Notifications, User */}
{showSearch && (
setSearchQuery(e.target.value)} placeholder={searchPlaceholder} className=" w-64 pl-10 pr-4 py-2 text-sm bg-muted border border-border rounded-lg text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary " />
)} {rightContent} {onNotificationClick && ( )} {user && ( {user.imageUrl ? ( ) : (
{user.name?.[0]?.toUpperCase() || user.email?.[0]?.toUpperCase() || '?'}
)} {user.name || user.email} } >
{user.name}
{user.email && (
{user.email}
)}
{userMenuItems.map((item) => ( ))}
)}
{navItems.length > 0 && (
{navItems.map((item) => { const isActive = activeId === item.id return ( ) })}
)}
) }