"use client"; import { useRef, useEffect, useCallback } from "react"; import { useRouter, usePathname } from "next/navigation"; import { motion } from "framer-motion"; import { Search, CornerDownLeft } from "lucide-react"; import { cn } from "@/lib/utils"; /** * HeaderSearchButton — a search icon in the page header that expands * into a full search input on click. Typing a query and pressing Enter * (or clicking the search icon again) navigates to /search?q=... * * State is controlled by the parent (PageHeader) so the header can * hide/show adjacent elements (logo, reading settings) on mobile. * * Pressing Escape or clicking outside collapses the input. * Keyboard shortcut: "/" focuses the search from anywhere on the page. */ /** Map the current pathname to the search `type` filter value. */ function searchTypeFromPath(pathname: string): string | null { if (pathname.startsWith("/books")) return "books"; if (pathname.startsWith("/letters")) return "letters"; if (pathname.startsWith("/essays")) return "essays"; if (pathname.startsWith("/transcriptions")) return "transcriptions"; if (pathname.startsWith("/glossary")) return "glossary"; if (pathname.startsWith("/journals")) return "journals"; if (pathname.startsWith("/magazines")) return "magazines"; if (pathname.startsWith("/other-documents")) return "other_documents"; return null; // homepage, search page, about, etc. → "all" } export function HeaderSearchButton({ expanded, onExpandedChange, }: { expanded: boolean; onExpandedChange: (expanded: boolean) => void; }) { const queryRef = useRef(""); const inputRef = useRef(null); const containerRef = useRef(null); const router = useRouter(); const pathname = usePathname(); // Focus input when expanded useEffect(() => { if (expanded && inputRef.current) { const timer = setTimeout(() => inputRef.current?.focus(), 80); return () => clearTimeout(timer); } }, [expanded]); // Close on outside click useEffect(() => { if (!expanded) return; function handleClick(e: MouseEvent) { if ( containerRef.current && !containerRef.current.contains(e.target as Node) ) { onExpandedChange(false); } } document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, [expanded, onExpandedChange]); // Global "/" shortcut to open search useEffect(() => { function handleKeyDown(e: KeyboardEvent) { const target = e.target as HTMLElement; if ( target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable ) { return; } if (e.key === "/") { e.preventDefault(); onExpandedChange(true); } } document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [onExpandedChange]); const handleSubmit = useCallback(() => { const trimmed = queryRef.current.trim(); const params = new URLSearchParams(); if (trimmed) params.set("q", trimmed); // Pre-select the search filter matching the current section const type = searchTypeFromPath(pathname); if (type) params.set("type", type); const qs = params.toString(); router.push(`/search${qs ? `?${qs}` : ""}`); onExpandedChange(false); queryRef.current = ""; if (inputRef.current) inputRef.current.value = ""; }, [router, onExpandedChange, pathname]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); handleSubmit(); } else if (e.key === "Escape") { e.preventDefault(); onExpandedChange(false); queryRef.current = ""; if (inputRef.current) inputRef.current.value = ""; } }, [handleSubmit, onExpandedChange], ); return (
{expanded ? ( { queryRef.current = e.target.value; }} onKeyDown={handleKeyDown} placeholder="Search..." className="flex-1 bg-transparent text-md md:text-sm text-foreground placeholder:text-muted-foreground outline-none min-w-0" /> ) : ( onExpandedChange(true)} aria-label="Search (press /)" title="Search (press /)" className={cn( "size-9 inline-flex items-center justify-center rounded-lg", "text-muted-foreground hover:text-foreground hover:bg-muted", "transition-colors outline-none", "focus-visible:ring-2 focus-visible:ring-ring", )} > )}
); }