{"version":3,"file":"BrazilStateCitySelect.cjs","names":[],"sources":["../../src/br/BrazilStateCitySelect.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — two dependent selects in one control, so every knob\n * comes in pairs: stateLabel and cityLabel, statePlaceholder and cityPlaceholder,\n * defaultUf and defaultCity. The pairing is the feature — the city list is derived\n * from the chosen UF, which is exactly what a caller wiring two separate selects has\n * to reimplement.\n */\nimport { useMemo, useState, type ReactElement } from \"react\";\nimport { Select } from \"@/components/Select\";\nimport {\n    administrativeRegionsByUf,\n    listStates,\n    municipalitiesByUf,\n    normalizeUf,\n    resolveMunicipality,\n    type Choice,\n    type UF,\n} from \"./locations\";\n\n/** Current selection emitted by {@link BrazilStateCitySelect}. */\nexport interface BrazilStateCitySelection {\n    /** Selected federative unit, or `null` when none. */\n    uf: UF | null;\n    /** Selected city, or `null` when none. */\n    city: string | null;\n    /**\n     * IBGE code of the selected municipality, or `null` when none is selected.\n     *\n     * This is the value to store. It survives the renames `city` does not, and\n     * it is what `geocodeMunicipality` and every other dataset here join on. A\n     * DF administrative region carries Brasília's code, because that is the\n     * municipality it is part of.\n     */\n    municipalityId: string | null;\n}\n\nexport interface BrazilStateCitySelectProps {\n    /** Pre-selected UF (uncontrolled initial value). */\n    defaultUf?: UF;\n    /** Pre-selected city (uncontrolled initial value). */\n    defaultCity?: string;\n    /** Fired whenever the state or city changes. */\n    onChange?: (selection: BrazilStateCitySelection) => void;\n    /** Label for the state select. Default: `\"Estado\"`. */\n    stateLabel?: string;\n    /** Label for the city select. Default: `\"Cidade\"`. */\n    cityLabel?: string;\n    /** Placeholder for the state select. Default: `\"Selecione o estado\"`. */\n    statePlaceholder?: string;\n    /** Placeholder for the city select. Default: `\"Selecione a cidade\"`. */\n    cityPlaceholder?: string;\n    /** Disable both selects. */\n    disabled?: boolean;\n    /** Layout of the two selects. Default: `\"row\"`. */\n    layout?: \"row\" | \"column\";\n}\n\n/**\n * Cascading Estado → Cidade selector backed by the bundled BR locations data —\n * pick a state and the city list narrows to that UF's municipalities. No\n * network, no external API. Complements {@link BrazilMap} (wire the map's\n * `onSelect` to drive the same UF).\n *\n * @example\n * <BrazilStateCitySelect onChange={({ uf, city }) => console.log(uf, city)} />\n */\n/**\n * The pickable places of a federative unit: its municipalities, plus the DF's\n * administrative regions.\n *\n * The DF has one municipality and nobody there writes \"Brasília\" in an address\n * field — they write Ceilândia, Taguatinga, Gama. Offering only the municipality\n * would be faithful to IBGE and useless in a form, so the 35 regions are listed\n * beside it and every one of them resolves to Brasília's code.\n *\n * @param uf - The selected federative unit.\n * @returns `{ value, label }` options, alphabetically, value === label.\n */\nfunction placeChoices(uf: UF): Choice[] {\n    const names = [\n        ...municipalitiesByUf(uf).map((m) => m.name),\n        ...administrativeRegionsByUf(uf).map((r) => r.name),\n    ].sort((a, b) => a.localeCompare(b, \"pt-BR\"));\n    return names.map((name) => ({ value: name, label: name }));\n}\n\nexport function BrazilStateCitySelect({\n    defaultUf,\n    defaultCity,\n    onChange,\n    stateLabel = \"Estado\",\n    cityLabel = \"Cidade\",\n    statePlaceholder = \"Selecione o estado\",\n    cityPlaceholder = \"Selecione a cidade\",\n    disabled = false,\n    layout = \"row\",\n}: BrazilStateCitySelectProps): ReactElement {\n    const [uf, setUf] = useState<UF | null>(defaultUf ?? null);\n    const [city, setCity] = useState<string | null>(defaultCity ?? null);\n\n    const stateOptions = useMemo(\n        () => listStates().map((s) => ({ value: s.uf, label: s.name })),\n        [],\n    );\n    const cityOptions = useMemo(() => (uf ? placeChoices(uf) : []), [uf]);\n\n    function handleUf(next: string): void {\n        const nextUf = normalizeUf(next);\n        setUf(nextUf);\n        setCity(null);\n        onChange?.({ uf: nextUf, city: null, municipalityId: null });\n    }\n\n    function handleCity(next: string): void {\n        const nextCity = next || null;\n        setCity(nextCity);\n        onChange?.({\n            uf,\n            city: nextCity,\n            municipalityId: uf && nextCity ? (resolveMunicipality(uf, nextCity)?.id ?? null) : null,\n        });\n    }\n\n    return (\n        <div\n            style={{\n                display: \"flex\",\n                flexDirection: layout === \"row\" ? \"row\" : \"column\",\n                gap: \"var(--tempest-space-3, 12px)\",\n                flexWrap: \"wrap\",\n            }}\n        >\n            <Select\n                label={stateLabel}\n                placeholder={statePlaceholder}\n                options={stateOptions}\n                value={uf ?? \"\"}\n                disabled={disabled}\n                onChange={(e) => handleUf(e.target.value)}\n                style={{ minWidth: 0 }}\n            />\n            <Select\n                label={cityLabel}\n                placeholder={cityPlaceholder}\n                options={cityOptions}\n                value={city ?? \"\"}\n                disabled={disabled || !uf}\n                onChange={(e) => handleCity(e.target.value)}\n                style={{ minWidth: 0 }}\n            />\n        </div>\n    );\n}\n"],"mappings":"sIA8EA,SAAS,EAAa,EAAkB,CAKpC,MAJc,CACV,GAAG,EAAA,mBAAmB,CAAE,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,EAC3C,GAAG,EAAA,0BAA0B,CAAE,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,CACtD,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,cAAc,EAAG,OAAO,CACpC,CAAA,CAAM,IAAK,IAAU,CAAE,MAAO,EAAM,MAAO,CAAK,EAAE,CAC7D,CAEA,SAAgB,EAAsB,CAClC,YACA,cACA,WACA,aAAa,SACb,YAAY,SACZ,mBAAmB,qBACnB,kBAAkB,qBAClB,WAAW,GACX,SAAS,OACgC,CACzC,GAAM,CAAC,EAAI,IAAA,EAAS,EAAA,SAAA,CAAoB,GAAa,IAAI,EACnD,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAwB,GAAe,IAAI,EAE7D,GAAA,EAAe,EAAA,QAAA,KACX,EAAA,WAAW,CAAC,CAAC,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,EAC9D,CAAC,CACL,EACM,GAAA,EAAc,EAAA,QAAA,KAAe,EAAK,EAAa,CAAE,EAAI,CAAC,EAAI,CAAC,CAAE,CAAC,EAEpE,SAAS,EAAS,EAAoB,CAClC,IAAM,EAAS,EAAA,YAAY,CAAI,EAC/B,EAAM,CAAM,EACZ,EAAQ,IAAI,EACZ,IAAW,CAAE,GAAI,EAAQ,KAAM,KAAM,eAAgB,IAAK,CAAC,CAC/D,CAEA,SAAS,EAAW,EAAoB,CACpC,IAAM,EAAW,GAAQ,KACzB,EAAQ,CAAQ,EAChB,IAAW,CACP,KACA,KAAM,EACN,eAAgB,GAAM,EAAY,EAAA,oBAAoB,EAAI,CAAQ,CAAC,EAAE,IAAM,KAAQ,IACvF,CAAC,CACL,CAEA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,MAAO,CACH,QAAS,OACT,cAAe,IAAW,MAAQ,MAAQ,SAC1C,IAAK,+BACL,SAAU,MACd,EANJ,SAAA,EAQI,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,MAAO,EACP,YAAa,EACb,QAAS,EACT,MAAO,GAAM,GACH,WACV,SAAW,GAAM,EAAS,EAAE,OAAO,KAAK,EACxC,MAAO,CAAE,SAAU,CAAE,CACxB,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,MAAO,EACP,YAAa,EACb,QAAS,EACT,MAAO,GAAQ,GACf,SAAU,GAAY,CAAC,EACvB,SAAW,GAAM,EAAW,EAAE,OAAO,KAAK,EAC1C,MAAO,CAAE,SAAU,CAAE,CACxB,CAAA,CACA,GAEb"}