/* global React */ const { useState, useEffect, useRef, useMemo } = React; // ============================================================ // IBGE autocomplete: state first → city dependent. // ============================================================ window.LocationFields = function LocationFields({ uf, setUf, city, setCity, errors, onTabFromCity }) { const [states, setStates] = useState([]); const [cities, setCities] = useState([]); const [ufQuery, setUfQuery] = useState(uf?.label || ""); const [ufOpen, setUfOpen] = useState(false); const [ufActive, setUfActive] = useState(0); const [cityQuery, setCityQuery] = useState(city || ""); const [cityOpen, setCityOpen] = useState(false); const [cityActive, setCityActive] = useState(0); const [loadingCities, setLoadingCities] = useState(false); const ufInputRef = useRef(null); const cityInputRef = useRef(null); // Load states once useEffect(() => { fetch("https://servicodosestados.ibge.gov.br/api/v1/localidades/estados?orderBy=nome") .catch(() => fetch("https://servicodados.ibge.gov.br/api/v1/localidades/estados?orderBy=nome") ) .then((r) => r.json()) .then((data) => setStates(data)) .catch(() => { // Fallback: hardcoded UF list setStates(FALLBACK_UFS); }); }, []); // Load cities when UF changes useEffect(() => { if (!uf?.id) { setCities([]); return; } setLoadingCities(true); fetch(`https://servicodados.ibge.gov.br/api/v1/localidades/estados/${uf.id}/municipios?orderBy=nome`) .then((r) => r.json()) .then((data) => setCities(data.map((c) => c.nome))) .catch(() => setCities([])) .finally(() => setLoadingCities(false)); }, [uf?.id]); // ----- UF filter const ufMatches = useMemo(() => { const q = ufQuery.trim().toLowerCase(); if (!q) return states.slice(0, 12); return states .filter( (s) => s.nome.toLowerCase().includes(q) || s.sigla.toLowerCase().includes(q) ) .slice(0, 8); }, [ufQuery, states]); // ----- City filter const cityMatches = useMemo(() => { const q = cityQuery.trim().toLowerCase(); if (!q) return cities.slice(0, 8); return cities.filter((c) => c.toLowerCase().includes(q)).slice(0, 8); }, [cityQuery, cities]); function pickUf(s) { setUf({ id: s.id, sigla: s.sigla, nome: s.nome, label: `${s.nome} (${s.sigla})` }); setUfQuery(`${s.nome} (${s.sigla})`); setUfOpen(false); setCity(""); setCityQuery(""); // jump to city setTimeout(() => cityInputRef.current?.focus(), 30); } function pickCity(name) { setCity(name); setCityQuery(name); setCityOpen(false); setTimeout(() => onTabFromCity?.(), 30); } function ufKeyDown(e) { if (!ufOpen && (e.key === "ArrowDown" || e.key === "Enter")) { setUfOpen(true); return; } if (e.key === "ArrowDown") { e.preventDefault(); setUfActive((i) => Math.min(i + 1, ufMatches.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setUfActive((i) => Math.max(i - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); const pick = ufMatches[ufActive]; if (pick) pickUf(pick); } else if (e.key === "Escape") { setUfOpen(false); } else if (e.key === "Tab" && ufOpen && ufMatches[ufActive]) { // soft autocomplete on Tab pickUf(ufMatches[ufActive]); } } function cityKeyDown(e) { if (!cityOpen && (e.key === "ArrowDown" || e.key === "Enter")) { setCityOpen(true); return; } if (e.key === "ArrowDown") { e.preventDefault(); setCityActive((i) => Math.min(i + 1, cityMatches.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setCityActive((i) => Math.max(i - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); const pick = cityMatches[cityActive]; if (pick) pickCity(pick); } else if (e.key === "Escape") { setCityOpen(false); } else if (e.key === "Tab" && cityOpen && cityMatches[cityActive]) { pickCity(cityMatches[cityActive]); } } return (
{ setUfQuery(e.target.value); setUfOpen(true); setUfActive(0); if (uf) setUf(null); }} onFocus={() => setUfOpen(true)} onBlur={() => setTimeout(() => setUfOpen(false), 120)} onKeyDown={ufKeyDown} autoComplete="off" /> {ufOpen && ufMatches.length > 0 && (
{ufMatches.map((s, i) => (
e.preventDefault()} onClick={() => pickUf(s)} onMouseEnter={() => setUfActive(i)} > {s.nome} ({s.sigla})
))}
)} {ufOpen && ufQuery && ufMatches.length === 0 && (
Nenhum estado encontrado.
)}
{errors.uf || ""}
{ setCityQuery(e.target.value); setCityOpen(true); setCityActive(0); setCity(e.target.value); }} onFocus={() => setCityOpen(true)} onBlur={() => setTimeout(() => setCityOpen(false), 120)} onKeyDown={cityKeyDown} autoComplete="off" /> {cityOpen && cityMatches.length > 0 && (
{cityMatches.map((name, i) => (
e.preventDefault()} onClick={() => pickCity(name)} onMouseEnter={() => setCityActive(i)} > {name}
))}
)} {cityOpen && uf && cityQuery && cityMatches.length === 0 && !loadingCities && (
Nenhuma cidade encontrada.
)}
{errors.city || ""}
); }; const FALLBACK_UFS = [ { id: 12, sigla: "AC", nome: "Acre" }, { id: 27, sigla: "AL", nome: "Alagoas" }, { id: 16, sigla: "AP", nome: "Amapá" }, { id: 13, sigla: "AM", nome: "Amazonas" }, { id: 29, sigla: "BA", nome: "Bahia" }, { id: 23, sigla: "CE", nome: "Ceará" }, { id: 53, sigla: "DF", nome: "Distrito Federal" }, { id: 32, sigla: "ES", nome: "Espírito Santo" }, { id: 52, sigla: "GO", nome: "Goiás" }, { id: 21, sigla: "MA", nome: "Maranhão" }, { id: 51, sigla: "MT", nome: "Mato Grosso" }, { id: 50, sigla: "MS", nome: "Mato Grosso do Sul" }, { id: 31, sigla: "MG", nome: "Minas Gerais" }, { id: 15, sigla: "PA", nome: "Pará" }, { id: 25, sigla: "PB", nome: "Paraíba" }, { id: 41, sigla: "PR", nome: "Paraná" }, { id: 26, sigla: "PE", nome: "Pernambuco" }, { id: 22, sigla: "PI", nome: "Piauí" }, { id: 33, sigla: "RJ", nome: "Rio de Janeiro" }, { id: 24, sigla: "RN", nome: "Rio Grande do Norte" }, { id: 43, sigla: "RS", nome: "Rio Grande do Sul" }, { id: 11, sigla: "RO", nome: "Rondônia" }, { id: 14, sigla: "RR", nome: "Roraima" }, { id: 42, sigla: "SC", nome: "Santa Catarina" }, { id: 35, sigla: "SP", nome: "São Paulo" }, { id: 28, sigla: "SE", nome: "Sergipe" }, { id: 17, sigla: "TO", nome: "Tocantins" }, ];