"use client"; import { useEffect, useRef, useState } from "react"; import { LANGUAGES } from "@/lib/languages"; import { Search } from "lucide-react"; /** * Searchable language dropdown used by the per-message translator and the * composer's translate-before-send flow. Positions itself above or below the * trigger depending on available space (like a tooltip). */ export function LanguageMenu({ onPick, onClose, align = "left", }: { onPick: (langName: string) => void; onClose: () => void; align?: "left" | "right"; }) { const [q, setQ] = useState(""); const ref = useRef(null); const [vpos, setVpos] = useState<"up" | "down">("down"); useEffect(() => { const parent = ref.current?.offsetParent as HTMLElement | null; const r = (parent ?? ref.current)?.getBoundingClientRect(); if (r) { const H = 320; setVpos(r.bottom + H > window.innerHeight && r.top > H ? "up" : "down"); } }, []); useEffect(() => { const onDoc = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) onClose(); }; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") { e.stopPropagation(); onClose(); } }; document.addEventListener("mousedown", onDoc); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("keydown", onKey); }; }, [onClose]); const list = LANGUAGES.filter( (l) => l.name.toLowerCase().includes(q.toLowerCase()) || l.native.toLowerCase().includes(q.toLowerCase()), ); return (
setQ(e.target.value)} placeholder="Search language…" className="w-full bg-transparent text-[13px] outline-none placeholder:text-dim" />
{list.map((l) => ( ))} {list.length === 0 &&
No match.
}
); }