/* 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 (