import { useEffect, useRef, useState, type ReactNode } from 'react'; type Choice = { id: string; name: string; source_code?: string | null }; /** One search field owns the list and commits only an explicitly chosen option. */ export default function TerritorySearchSelect({ id, label, search, onSearch, options, selected, onChange, disabled = false, loading = false, error, required = false, className, placeholder, children, }: { id: string; label: string; search: string; onSearch: (query: string) => void; options: T[]; selected?: T; onChange: (option?: T) => void; disabled?: boolean; loading?: boolean; error?: string; required?: boolean; className?: string; placeholder?: string; children?: ReactNode; }) { const [open, setOpen] = useState(false); const [active, setActive] = useState(-1); const activeRef = useRef(null); const expanded = open && !disabled; const listId = `${id}-options`; useEffect(() => { if (expanded) activeRef.current?.scrollIntoView({ block: 'nearest' }); }, [active, expanded]); const close = () => { setOpen(false); setActive(-1); onSearch(''); }; const choose = (option?: T) => { onChange(option); close(); }; return (
{ if ( !event.currentTarget.contains( event.relatedTarget as Node | null, ) ) close(); }} > { setOpen(true); setActive(-1); onSearch(''); }} onClick={() => setOpen(true)} onChange={(event) => { setOpen(true); setActive(-1); onSearch(event.target.value); }} onKeyDown={(event) => { if (event.key === 'Escape') { event.preventDefault(); close(); } if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { event.preventDefault(); setOpen(true); setActive((current) => !options.length ? -1 : event.key === 'ArrowDown' ? Math.min(current + 1, options.length - 1) : current < 0 ? options.length - 1 : Math.max(0, current - 1), ); } if (event.key === 'Enter' && expanded) { event.preventDefault(); if (!loading && options[active < 0 ? 0 : active]) choose(options[active < 0 ? 0 : active]); } }} /> {selected && !disabled && ( )} {error && ( )} {expanded && (
{loading && (

A carregar…

)} {!loading && options.length === 0 && (

{error ? 'Não foi possível carregar as opções.' : 'Sem resultados para esta pesquisa.'}

)} {options.map((option, index) => ( ))}
{children}
)}
); }