import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { cn } from '@/lib/utils'; import { getAllCountries, flagEmojiFromCode, matchCountry } from '@/lib/countries'; /** * Table cell: shows flag only; opens searchable list of countries (full names). */ export function SearchableCountryPicker({ value, onChange, className = '' }) { const countries = useMemo(() => getAllCountries(), []); const [open, setOpen] = useState(false); const [query, setQuery] = useState(''); const triggerRef = useRef(null); const menuRef = useRef(null); const inputRef = useRef(null); const [pos, setPos] = useState({ top: 0, left: 0, width: 320 }); const matched = matchCountry(value || '', countries); const flag = matched ? flagEmojiFromCode(matched.code) : '🏳️'; const title = matched ? matched.name : value || 'Select country'; const filtered = useMemo(() => { const q = query.trim().toLowerCase(); if (!q) return countries; return countries.filter( (c) => c.name.toLowerCase().includes(q) || c.code.toLowerCase().includes(q) ); }, [countries, query]); const computeMenuPosition = () => { const trigger = triggerRef.current; if (!trigger) return; const r = trigger.getBoundingClientRect(); const PAD = 12; const MENU_W = 320; const vw = window.innerWidth; const vh = window.innerHeight; // Last-column friendly: if trigger sits in the right part of the viewport, // anchor the panel by its right edge to the trigger's right edge so the menu opens leftward. const inRightZone = r.right > vw * 0.55; let left = inRightZone ? r.right - MENU_W : r.left; left = Math.max(PAD, Math.min(left, vw - MENU_W - PAD)); const estPanelH = Math.min(vh * 0.72, 420); let top = r.bottom + 6; if (top + estPanelH > vh - PAD) { top = Math.max(PAD, r.top - estPanelH - 6); } setPos({ top, left, width: MENU_W }); }; useLayoutEffect(() => { if (!open || !triggerRef.current) return; computeMenuPosition(); }, [open]); useEffect(() => { if (!open) return; const reposition = () => computeMenuPosition(); window.addEventListener('scroll', reposition, true); window.addEventListener('resize', reposition); return () => { window.removeEventListener('scroll', reposition, true); window.removeEventListener('resize', reposition); }; }, [open]); useEffect(() => { if (!open) return; const t = requestAnimationFrame(() => inputRef.current?.focus()); return () => cancelAnimationFrame(t); }, [open]); useEffect(() => { if (!open) return; const onPointerDown = (e) => { const tr = triggerRef.current; const menu = menuRef.current; if (tr?.contains(e.target) || menu?.contains(e.target)) return; setOpen(false); setQuery(''); }; document.addEventListener('pointerdown', onPointerDown); return () => document.removeEventListener('pointerdown', onPointerDown); }, [open]); const pick = (row) => { onChange(row.name); setOpen(false); setQuery(''); }; return (