"use client"; import { useRef, useState, type FocusEvent, type FormEvent, type KeyboardEvent } from "react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; type LiquidSearchProps = { /** Called with the trimmed text when the search is sent (Enter or the search button). */ onSearch?: (query: string) => void; /** Hint text in the open box. */ placeholder?: string; /** Name read out by screen readers. */ label?: string; /** Width in px when open. Never wider than its container. */ width?: number; /** Start open. */ defaultOpen?: boolean; /** Glow colour when open. */ accent?: string; /** Box look. */ className?: string; }; function SearchIcon() { return ( ); } /* LiquidSearch: a round search button that stretches into a search box with a wobbly, liquid spring. Esc or clicking away (when empty) folds it back. */ export function LiquidSearch({ onSearch, placeholder = "Search…", label = "Search", width = 320, defaultOpen = false, accent = "#a78bfa", className = "border border-white/12 bg-[#141026] text-white", }: LiquidSearchProps) { const reduce = useReducedMotion(); const [open, setOpen] = useState(defaultOpen); const [query, setQuery] = useState(""); const input = useRef(null); const button = useRef(null); const spring = reduce ? { duration: 0 } : { type: "spring" as const, stiffness: 360, damping: 22, mass: 0.9 }; const expand = () => { setOpen(true); requestAnimationFrame(() => input.current?.focus()); }; const collapse = (refocus: boolean) => { setOpen(false); setQuery(""); if (refocus) button.current?.focus(); }; const submit = (e: FormEvent) => { e.preventDefault(); if (!open) return expand(); const q = query.trim(); if (q) onSearch?.(q); else input.current?.focus(); }; const onKey = (e: KeyboardEvent) => { if (e.key !== "Escape") return; e.preventDefault(); if (query) setQuery(""); else collapse(true); }; const onBlur = (e: FocusEvent) => { if (!e.currentTarget.contains(e.relatedTarget as Node | null) && !query.trim()) collapse(false); }; return ( setQuery(e.target.value)} onKeyDown={onKey} placeholder={placeholder} aria-label={label} enterKeyHint="search" className="h-12 w-full min-w-0 bg-transparent text-base text-white outline-none placeholder:text-white/35 [&::-webkit-search-cancel-button]:appearance-none" /> {open && query && ( { setQuery(""); input.current?.focus(); }} initial={{ scale: 0, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} exit={{ scale: 0, opacity: 0 }} transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 500, damping: 28 }} className="grid size-9 shrink-0 place-items-center rounded-full text-white/60 outline-none hover:bg-white/10 hover:text-white focus-visible:ring-2 focus-visible:ring-white/70" > )} ); }