Files
message-inbox-web-frontend-sdk/apps/web/components/message/LanguageMenu.tsx
T
2026-07-17 21:48:37 +05:30

78 lines
2.8 KiB
TypeScript

"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<HTMLDivElement>(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 (
<div
ref={ref}
className={`absolute z-50 w-64 overflow-hidden rounded-control border border-border bg-elevated shadow-pop animate-slide-up ${vpos === "up" ? "bottom-9" : "top-9"} ${align === "right" ? "right-0" : "left-0"}`}
>
<div className="flex items-center gap-2 border-b border-border px-2.5 py-2">
<Search size={14} className="text-dim" />
<input
autoFocus
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Search language…"
className="w-full bg-transparent text-[13px] outline-none placeholder:text-dim"
/>
</div>
<div className="max-h-64 overflow-y-auto py-1">
{list.map((l) => (
<button
key={l.name}
onClick={() => onPick(l.name)}
className="flex w-full items-center gap-2.5 px-3 py-1.5 text-left text-[13px] hover:bg-hover"
>
<span className="text-base">{l.flag}</span>
<span className="font-medium">{l.name}</span>
<span className="ml-auto text-[11.5px] text-dim">{l.native}</span>
</button>
))}
{list.length === 0 && <div className="px-3 py-4 text-center text-[12.5px] text-muted">No match.</div>}
</div>
</div>
);
}