spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1'use client';2import { Menu, Search, X } from 'lucide-react';3import Link from 'next/link';4import { usePathname } from 'next/navigation';5import { useEffect, useState } from 'react';6import { Wordmark } from '@/components/brand/logo';7import { cn } from '@/lib/cn';8import { moreNav, primaryNav, routes } from '@/lib/site';9import { useOpenSearch } from './search-context';1011export function SiteHeader() {12 const pathname = usePathname();13 const openSearch = useOpenSearch();14 const [menu, setMenu] = useState(false);15 useEffect(() => setMenu(false), [pathname]);16 const isHome = pathname === '/';17 const isExplore = pathname === '/explore';1819 return (20 <header className={cn('sticky top-0 z-40 border-b border-rule backdrop-blur-md', isHome || isExplore ? 'bg-space/70' : 'bg-space/85')}>21 <div className="container-x mx-auto flex h-[60px] max-w-[1600px] items-center gap-4">22 <Link href={routes.home()} className="flex h-11 items-center" aria-label="SatelliteIndex home">23 <Wordmark />24 </Link>25 <nav aria-label="Primary" className="ml-2 hidden items-center gap-0.5 lg:flex">26 {primaryNav.map((n) => {27 const active = pathname === n.href || (n.href !== '/' && pathname.startsWith(n.href + '/')) || (n.href === '/satellites' && pathname.startsWith('/satellite/')) || (n.href === '/constellations' && pathname.startsWith('/constellation/')) || (n.href === '/operators' && pathname.startsWith('/operator/')) || (n.href === '/countries' && pathname.startsWith('/country/')) || (n.href === '/launches' && pathname.startsWith('/launch/'));28 return (29 <Link key={n.href} href={n.href} aria-current={active ? 'page' : undefined} className={cn('rounded-md px-3 py-2 text-[13.5px] transition-colors', active ? 'bg-plane-2 text-ink' : 'text-ink-2 hover:bg-plane-2 hover:text-ink')}>30 {n.label}31 </Link>32 );33 })}34 </nav>35 <div className="ml-auto flex items-center gap-2">36 <button37 type="button"38 onClick={openSearch}39 className="hidden h-10 min-w-[260px] items-center gap-2 rounded-md border border-rule bg-plane-2/70 px-3 text-sm text-ink-3 hover:border-rule-strong hover:text-ink-2 md:flex"40 aria-label="Open search"41 >42 <Search className="size-4" aria-hidden />43 <span className="flex-1 text-left">Search satellites, operators, IDs…</span>44 <kbd className="mono rounded border border-rule px-1.5 py-0.5 text-[10px]">⌘K</kbd>45 </button>46 <button type="button" onClick={openSearch} className="flex size-11 items-center justify-center rounded-md text-ink-2 hover:bg-plane-2 hover:text-ink md:hidden" aria-label="Open search">47 <Search className="size-5" aria-hidden />48 </button>49 <button type="button" onClick={() => setMenu((m) => !m)} className="flex size-11 items-center justify-center rounded-md text-ink-2 hover:bg-plane-2 hover:text-ink lg:size-10" aria-label={menu ? 'Close menu' : 'Open menu'} aria-expanded={menu}>50 {menu ? <X className="size-5" aria-hidden /> : <Menu className="size-5" aria-hidden />}51 </button>52 </div>53 </div>54 {menu && (55 <div className="border-t border-rule bg-plane/95 backdrop-blur-md">56 <div className="container-x mx-auto grid max-w-[1600px] gap-6 py-5 md:grid-cols-3">57 <div>58 <p className="eyebrow mb-2">Explore</p>59 <ul className="grid grid-cols-2 gap-1 md:grid-cols-1">60 {primaryNav.map((n) => (61 <li key={n.href}>62 <Link href={n.href} className="block rounded-md px-2 py-2.5 text-[15px] text-ink-2 hover:bg-plane-2 hover:text-ink">{n.label}</Link>63 </li>64 ))}65 </ul>66 </div>67 <div>68 <p className="eyebrow mb-2">More</p>69 <ul className="grid grid-cols-2 gap-1 md:grid-cols-1">70 {moreNav.map((n) => (71 <li key={n.href}>72 <Link href={n.href} className="block rounded-md px-2 py-2.5 text-[15px] text-ink-2 hover:bg-plane-2 hover:text-ink">{n.label}</Link>73 </li>74 ))}75 </ul>76 </div>77 <div className="hidden md:block">78 <p className="eyebrow mb-2">About the index</p>79 <p className="text-sm leading-relaxed text-ink-2">80 SatelliteIndex aggregates public orbital, catalog and operator data into one canonical, source-attributed database. Positions are propagated with SGP4 from public element sets.81 </p>82 <Link href={routes.methodology()} className="mt-3 inline-block text-sm text-accent hover:underline">Read the methodology →</Link>83 </div>84 </div>85 </div>86 )}87 </header>88 );89}90