tempest-react-sdk 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/br.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"br.cjs","sources":["../src/br/locations.ts","../src/br/state-geo.ts","../src/br/svg-utils.ts","../src/br/BrazilMap.tsx","../src/br/BrazilStateMap.tsx","../src/br/BrazilStateCitySelect.tsx","../src/br/index.ts"],"sourcesContent":["import rawLocations from \"./data/br-locations.json\";\n\n/** The 27 Brazilian federative units (26 states + Federal District). */\nexport type UF =\n | \"AC\"\n | \"AL\"\n | \"AP\"\n | \"AM\"\n | \"BA\"\n | \"CE\"\n | \"DF\"\n | \"ES\"\n | \"GO\"\n | \"MA\"\n | \"MT\"\n | \"MS\"\n | \"MG\"\n | \"PA\"\n | \"PB\"\n | \"PR\"\n | \"PE\"\n | \"PI\"\n | \"RJ\"\n | \"RN\"\n | \"RS\"\n | \"RO\"\n | \"RR\"\n | \"SC\"\n | \"SP\"\n | \"SE\"\n | \"TO\";\n\n/** The five Brazilian macro-regions (IBGE). */\nexport type BrRegion = \"Norte\" | \"Nordeste\" | \"Centro-Oeste\" | \"Sudeste\" | \"Sul\";\n\n/** A federative unit with its display name and city list. */\nexport interface BrazilState {\n /** Two-letter acronym, e.g. `\"SP\"`. */\n uf: UF;\n /** Full name, e.g. `\"São Paulo\"`. */\n name: string;\n /** Macro-region the state belongs to. */\n region: BrRegion;\n /** City names within the state, alphabetically. */\n cities: string[];\n}\n\n/** A `{ value, label }` option, handy for `<Select>` / `<Combobox>`. */\nexport interface Choice {\n value: string;\n label: string;\n}\n\n/** IBGE macro-region of each federative unit. */\nconst REGION_BY_UF: Record<UF, BrRegion> = {\n AC: \"Norte\",\n AP: \"Norte\",\n AM: \"Norte\",\n PA: \"Norte\",\n RO: \"Norte\",\n RR: \"Norte\",\n TO: \"Norte\",\n AL: \"Nordeste\",\n BA: \"Nordeste\",\n CE: \"Nordeste\",\n MA: \"Nordeste\",\n PB: \"Nordeste\",\n PE: \"Nordeste\",\n PI: \"Nordeste\",\n RN: \"Nordeste\",\n SE: \"Nordeste\",\n DF: \"Centro-Oeste\",\n GO: \"Centro-Oeste\",\n MT: \"Centro-Oeste\",\n MS: \"Centro-Oeste\",\n ES: \"Sudeste\",\n MG: \"Sudeste\",\n RJ: \"Sudeste\",\n SP: \"Sudeste\",\n PR: \"Sul\",\n RS: \"Sul\",\n SC: \"Sul\",\n};\n\ninterface RawState {\n sigla: string;\n estado: string;\n cidades: string[];\n}\n\nconst RAW = rawLocations as unknown as { dataLocals: RawState[] };\n\n/** Normalized, frozen list of all 27 states (built once at module load). */\nconst STATES: readonly BrazilState[] = RAW.dataLocals\n .map((entry) => {\n const uf = entry.sigla as UF;\n return {\n uf,\n name: entry.estado,\n region: REGION_BY_UF[uf],\n cities: entry.cidades,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name, \"pt-BR\"));\n\nconst STATE_BY_UF = new Map<UF, BrazilState>(STATES.map((s) => [s.uf, s]));\n\n/** All 27 federative units, sorted by name. */\nexport function listStates(): readonly BrazilState[] {\n return STATES;\n}\n\n/** Look up a single state by acronym (case-insensitive). Returns `null` if unknown. */\nexport function getState(uf: string): BrazilState | null {\n const normalized = normalizeUf(uf);\n return normalized ? (STATE_BY_UF.get(normalized) ?? null) : null;\n}\n\n/**\n * City names for a federative unit (case-insensitive acronym). Returns an empty\n * array for an unknown UF — \"no matches\" is a valid result, not an error.\n */\nexport function citiesByUf(uf: string): string[] {\n return getState(uf)?.cities ?? [];\n}\n\n/** States belonging to a macro-region. */\nexport function statesByRegion(region: BrRegion): readonly BrazilState[] {\n return STATES.filter((s) => s.region === region);\n}\n\n/** True when `value` is one of the 27 valid acronyms (case-insensitive). */\nexport function isValidUf(value: string): boolean {\n return normalizeUf(value) !== null;\n}\n\n/**\n * Normalize an acronym to canonical uppercase form, or `null` if it is not a\n * valid UF. `\"sp\"` → `\"SP\"`, `\"xx\"` → `null`.\n */\nexport function normalizeUf(value: string): UF | null {\n const upper = value.trim().toUpperCase();\n return (REGION_BY_UF as Record<string, BrRegion>)[upper] ? (upper as UF) : null;\n}\n\n/** True when `city` exists within `uf` (both case-insensitive). */\nexport function isValidCity(uf: string, city: string): boolean {\n const target = city.trim().toLowerCase();\n return citiesByUf(uf).some((c) => c.toLowerCase() === target);\n}\n\n/** `{ value: uf, label: name }` options for every state, for a `<Select>`. */\nexport function ufChoices(): Choice[] {\n return STATES.map((s) => ({ value: s.uf, label: s.name }));\n}\n\n/** `{ value, label }` options for every city in a UF (value === label). */\nexport function cityChoices(uf: string): Choice[] {\n return citiesByUf(uf).map((c) => ({ value: c, label: c }));\n}\n","import type { BrUfGeometry } from \"./br-geo\";\nimport type { UF } from \"./locations\";\n\n/** One municipality feature within a state. */\nexport interface MunicipalityFeature {\n type: \"Feature\";\n properties: {\n /** 7-digit IBGE municipality code. */\n id: string;\n /** Municipality name. */\n name: string;\n };\n geometry: BrUfGeometry;\n}\n\n/** Municipality boundaries for a single federative unit. */\nexport interface StateMunicipalities {\n type: \"FeatureCollection\";\n /** The federative unit these municipalities belong to. */\n uf: UF;\n features: MunicipalityFeature[];\n}\n\ntype GeoModule = { default: StateMunicipalities };\n\n// One explicit dynamic import per UF — each `data/mun/<UF>.json` becomes its own\n// lazy chunk, so `loadStateMunicipalities(\"SP\")` fetches only São Paulo's\n// geometry and the full ~2 MB municipal dataset never lands in one bundle.\nconst LOADERS: Record<UF, () => Promise<GeoModule>> = {\n AC: () => import(\"./data/mun/AC.json\") as unknown as Promise<GeoModule>,\n AL: () => import(\"./data/mun/AL.json\") as unknown as Promise<GeoModule>,\n AP: () => import(\"./data/mun/AP.json\") as unknown as Promise<GeoModule>,\n AM: () => import(\"./data/mun/AM.json\") as unknown as Promise<GeoModule>,\n BA: () => import(\"./data/mun/BA.json\") as unknown as Promise<GeoModule>,\n CE: () => import(\"./data/mun/CE.json\") as unknown as Promise<GeoModule>,\n DF: () => import(\"./data/mun/DF.json\") as unknown as Promise<GeoModule>,\n ES: () => import(\"./data/mun/ES.json\") as unknown as Promise<GeoModule>,\n GO: () => import(\"./data/mun/GO.json\") as unknown as Promise<GeoModule>,\n MA: () => import(\"./data/mun/MA.json\") as unknown as Promise<GeoModule>,\n MT: () => import(\"./data/mun/MT.json\") as unknown as Promise<GeoModule>,\n MS: () => import(\"./data/mun/MS.json\") as unknown as Promise<GeoModule>,\n MG: () => import(\"./data/mun/MG.json\") as unknown as Promise<GeoModule>,\n PA: () => import(\"./data/mun/PA.json\") as unknown as Promise<GeoModule>,\n PB: () => import(\"./data/mun/PB.json\") as unknown as Promise<GeoModule>,\n PR: () => import(\"./data/mun/PR.json\") as unknown as Promise<GeoModule>,\n PE: () => import(\"./data/mun/PE.json\") as unknown as Promise<GeoModule>,\n PI: () => import(\"./data/mun/PI.json\") as unknown as Promise<GeoModule>,\n RJ: () => import(\"./data/mun/RJ.json\") as unknown as Promise<GeoModule>,\n RN: () => import(\"./data/mun/RN.json\") as unknown as Promise<GeoModule>,\n RS: () => import(\"./data/mun/RS.json\") as unknown as Promise<GeoModule>,\n RO: () => import(\"./data/mun/RO.json\") as unknown as Promise<GeoModule>,\n RR: () => import(\"./data/mun/RR.json\") as unknown as Promise<GeoModule>,\n SC: () => import(\"./data/mun/SC.json\") as unknown as Promise<GeoModule>,\n SP: () => import(\"./data/mun/SP.json\") as unknown as Promise<GeoModule>,\n SE: () => import(\"./data/mun/SE.json\") as unknown as Promise<GeoModule>,\n TO: () => import(\"./data/mun/TO.json\") as unknown as Promise<GeoModule>,\n};\n\n/**\n * Lazily load the simplified municipality GeoJSON for a federative unit. Each\n * state's geometry is a separate chunk (~40-70 KB gzip), fetched on demand —\n * the full ~2 MB municipal dataset never lands in one bundle.\n *\n * @param uf - Federative unit acronym (e.g. `\"SP\"`).\n * @returns The municipality collection, or `null` for an unknown UF.\n *\n * @example\n * const sp = await loadStateMunicipalities(\"SP\");\n * sp?.features.length; // 644\n */\nexport async function loadStateMunicipalities(uf: UF): Promise<StateMunicipalities | null> {\n const loader = LOADERS[uf];\n if (!loader) return null;\n const mod = await loader();\n return mod.default ?? (mod as unknown as StateMunicipalities);\n}\n","import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoBounds } from \"@/geo/types\";\nimport type { BrUfGeometry, Ring } from \"./br-geo\";\n\n/** Iterate every ring of a geometry (Polygon or MultiPolygon). */\nexport function ringsOf(geometry: BrUfGeometry): Ring[] {\n return geometry.type === \"MultiPolygon\"\n ? (geometry.coordinates as Ring[][]).flat()\n : (geometry.coordinates as Ring[]);\n}\n\n/** Bounding box across every coordinate of the given geometries. */\nexport function geometriesBounds(geometries: readonly BrUfGeometry[]): GeoBounds {\n let minLat = 90;\n let maxLat = -90;\n let minLon = 180;\n let maxLon = -180;\n for (const geometry of geometries) {\n for (const ring of ringsOf(geometry)) {\n for (const [lon, lat] of ring) {\n if (lat < minLat) minLat = lat;\n if (lat > maxLat) maxLat = lat;\n if (lon < minLon) minLon = lon;\n if (lon > maxLon) maxLon = lon;\n }\n }\n }\n return { minLatitude: minLat, maxLatitude: maxLat, minLongitude: minLon, maxLongitude: maxLon };\n}\n\n/** Build the SVG `d` path for a geometry (all rings, `evenodd` handles holes). */\nexport function geometryPath(geometry: BrUfGeometry, projection: FittedProjection): string {\n return ringsOf(geometry)\n .map((ring) => {\n const pts = ring.map(([lon, lat]) => {\n const p = projection.project({ latitude: lat, longitude: lon });\n return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;\n });\n return `M${pts.join(\"L\")}Z`;\n })\n .join(\"\");\n}\n\n/** Rough visual centroid of a geometry (mean of its largest ring's points). */\nexport function geometryCentroid(\n geometry: BrUfGeometry,\n projection: FittedProjection,\n): { x: number; y: number } {\n const rings = ringsOf(geometry);\n const outer = rings.reduce((a, b) => (b.length > a.length ? b : a), rings[0] ?? []);\n let sx = 0;\n let sy = 0;\n for (const [lon, lat] of outer) {\n const p = projection.project({ latitude: lat, longitude: lon });\n sx += p.x;\n sy += p.y;\n }\n const n = outer.length || 1;\n return { x: sx / n, y: sy / n };\n}\n\n/** Linearly interpolate between two hex colors, returning an `rgb(...)` string. */\nexport function lerpColor(from: string, to: string, t: number): string {\n const parse = (c: string): [number, number, number] => {\n const hex = c.replace(\"#\", \"\");\n const full =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((ch) => ch + ch)\n .join(\"\")\n : hex;\n return [\n parseInt(full.slice(0, 2), 16),\n parseInt(full.slice(2, 4), 16),\n parseInt(full.slice(4, 6), 16),\n ];\n };\n const [r1, g1, b1] = parse(from);\n const [r2, g2, b2] = parse(to);\n const mix = (a: number, b: number): number => Math.round(a + (b - a) * t);\n return `rgb(${mix(r1, r2)}, ${mix(g1, g2)}, ${mix(b1, b2)})`;\n}\n","import { useEffect, useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { fitProjection } from \"@/geo/projection\";\nimport type { BrUfFeatureCollection } from \"./br-geo\";\nimport type { UF } from \"./locations\";\nimport { geometriesBounds, geometryCentroid, geometryPath, lerpColor } from \"./svg-utils\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface BrazilMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** Currently selected UF(s) — highlighted. Accepts one or many. */\n selected?: UF | readonly UF[] | null;\n /** Fired when a state is clicked. */\n onSelect?: (uf: UF) => void;\n /**\n * Optional choropleth values per UF. When set, each state is tinted between\n * `minColor` and `maxColor` by its value (linear). States without a value\n * use the base surface color.\n */\n values?: Partial<Record<UF, number>>;\n /** Choropleth low-end color. Default: a light primary tint. */\n minColor?: string;\n /** Choropleth high-end color. Default: the primary token. */\n maxColor?: string;\n /** Viewport height in pixels. Default: `440`. */\n height?: number;\n /** Inner padding in pixels. Default: `12`. */\n padding?: number;\n /** Render the UF acronym at each state centroid. Default: `true`. */\n showLabels?: boolean;\n /** Accessible label for the map region. Default: `\"Mapa do Brasil por estado\"`. */\n label?: string;\n /** Custom content when the geometry is still loading. */\n loadingContent?: ReactNode;\n}\n\n/**\n * Clickable choropleth map of Brazil's 27 federative units. Renders the bundled\n * simplified UF GeoJSON as SVG paths — **no external tiles or paid API**. Click\n * a state to fire `onSelect(uf)`; pass `selected` to highlight and `values` to\n * tint states by a metric.\n *\n * The GeoJSON (~36 KB gzip) is loaded lazily, so importing this component does\n * not pull the geometry until it actually mounts.\n *\n * @example\n * const [uf, setUf] = useState<UF | null>(null);\n * <BrazilMap selected={uf} onSelect={setUf} />\n *\n * @example\n * // Choropleth by a metric per state\n * <BrazilMap values={{ SP: 120, RJ: 90, MG: 60 }} />\n */\nexport function BrazilMap({\n selected,\n onSelect,\n values,\n minColor = \"#dbeafe\",\n maxColor = \"#2563eb\",\n height = 440,\n padding = 12,\n showLabels = true,\n label = \"Mapa do Brasil por estado\",\n loadingContent,\n className,\n style,\n ...rest\n}: BrazilMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n const [collection, setCollection] = useState<BrUfFeatureCollection | null>(null);\n\n useEffect(() => {\n let active = true;\n void import(\"./br-geo\").then((m) => {\n if (active) setCollection(m.BR_UF_GEOJSON);\n });\n return () => {\n active = false;\n };\n }, []);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const selectedSet = useMemo<Set<UF>>(() => {\n if (!selected) return new Set();\n return new Set(Array.isArray(selected) ? selected : [selected as UF]);\n }, [selected]);\n\n const valueRange = useMemo<[number, number] | null>(() => {\n if (!values) return null;\n const nums = Object.values(values).filter((v): v is number => typeof v === \"number\");\n if (nums.length === 0) return null;\n return [Math.min(...nums), Math.max(...nums)];\n }, [values]);\n\n const shapes = useMemo(() => {\n if (!collection) return null;\n const projection = fitProjection(\n geometriesBounds(collection.features.map((f) => f.geometry)),\n width,\n height,\n { padding },\n );\n return collection.features.map((feature) => ({\n uf: feature.properties.uf,\n name: feature.properties.name,\n d: geometryPath(feature.geometry, projection),\n centroid: geometryCentroid(feature.geometry, projection),\n }));\n }, [collection, width, height, padding]);\n\n function fillFor(uf: UF): string | undefined {\n if (!values || !valueRange) return undefined;\n const v = values[uf];\n if (typeof v !== \"number\") return undefined;\n const [min, max] = valueRange;\n const t = max === min ? 1 : (v - min) / (max - min);\n return lerpColor(minColor, maxColor, t);\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"group\"\n aria-label={label}\n {...rest}\n >\n {!shapes ? (\n <div className={styles.loading}>{loadingContent ?? \"Carregando mapa…\"}</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {shapes.map((shape) => {\n const isSelected = selectedSet.has(shape.uf);\n return (\n <path\n key={shape.uf}\n className={cn(styles.state, isSelected && styles.selected)}\n d={shape.d}\n fillRule=\"evenodd\"\n fill={isSelected ? undefined : fillFor(shape.uf)}\n data-uf={shape.uf}\n tabIndex={onSelect ? 0 : undefined}\n role={onSelect ? \"button\" : undefined}\n aria-label={shape.name}\n aria-pressed={onSelect ? isSelected : undefined}\n onClick={onSelect ? () => onSelect(shape.uf) : undefined}\n onKeyDown={\n onSelect\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect(shape.uf);\n }\n }\n : undefined\n }\n >\n <title>{shape.name}</title>\n </path>\n );\n })}\n\n {showLabels &&\n shapes.map((shape) => (\n <text\n key={`label-${shape.uf}`}\n className={styles.label}\n x={shape.centroid.x}\n y={shape.centroid.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n >\n {shape.uf}\n </text>\n ))}\n </svg>\n )}\n </div>\n );\n}\n","import { useEffect, useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { fitProjection } from \"@/geo/projection\";\nimport { getState, type UF } from \"./locations\";\nimport { loadStateMunicipalities, type StateMunicipalities } from \"./state-geo\";\nimport { geometriesBounds, geometryCentroid, geometryPath, lerpColor } from \"./svg-utils\";\nimport styles from \"./BrazilMap.module.css\";\n\n/** A municipality identified for selection callbacks. */\nexport interface Municipality {\n /** 7-digit IBGE code. */\n id: string;\n /** Municipality name. */\n name: string;\n}\n\nexport interface BrazilStateMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** Federative unit to draw (required). */\n uf: UF;\n /** Selected municipality — matched by IBGE `id` or by `name`. Accepts many. */\n selected?: string | readonly string[] | null;\n /** Fired when a municipality is clicked. */\n onSelect?: (municipality: Municipality) => void;\n /**\n * Choropleth values keyed by municipality `id` **or** `name`. When set, each\n * municipality is tinted between `minColor` and `maxColor`.\n */\n values?: Record<string, number>;\n /** Choropleth low-end color. Default: a light primary tint. */\n minColor?: string;\n /** Choropleth high-end color. Default: the primary token. */\n maxColor?: string;\n /** Viewport height in pixels. Default: `440`. */\n height?: number;\n /** Inner padding in pixels. Default: `12`. */\n padding?: number;\n /**\n * Render each municipality name at its centroid. Off by default — a state\n * can have hundreds of municipalities and labels overlap badly.\n */\n showLabels?: boolean;\n /** Accessible label. Default: derived from the state name. */\n label?: string;\n /** Content while the state geometry is loading. */\n loadingContent?: ReactNode;\n}\n\n/**\n * Clickable submap of a single Brazilian state showing **all its\n * municipalities** as SVG paths — no external tiles or paid API. The state's\n * geometry (~40-70 KB gzip) is loaded lazily per UF, so switching states fetches\n * only what is shown.\n *\n * @example\n * const [city, setCity] = useState<string | null>(null);\n * <BrazilStateMap uf=\"SP\" selected={city} onSelect={(m) => setCity(m.name)} />\n *\n * @example\n * // Choropleth of a metric by municipality name\n * <BrazilStateMap uf=\"RJ\" values={{ \"Rio de Janeiro\": 100, Niterói: 40 }} />\n */\nexport function BrazilStateMap({\n uf,\n selected,\n onSelect,\n values,\n minColor = \"#dbeafe\",\n maxColor = \"#2563eb\",\n height = 440,\n padding = 12,\n showLabels = false,\n label,\n loadingContent,\n className,\n style,\n ...rest\n}: BrazilStateMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n const [collection, setCollection] = useState<StateMunicipalities | null>(null);\n\n useEffect(() => {\n let active = true;\n setCollection(null);\n void loadStateMunicipalities(uf).then((data) => {\n if (active) setCollection(data);\n });\n return () => {\n active = false;\n };\n }, [uf]);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const selectedSet = useMemo<Set<string>>(() => {\n if (!selected) return new Set();\n return new Set(Array.isArray(selected) ? selected : [selected as string]);\n }, [selected]);\n\n const valueRange = useMemo<[number, number] | null>(() => {\n if (!values) return null;\n const nums = Object.values(values).filter((v): v is number => typeof v === \"number\");\n if (nums.length === 0) return null;\n return [Math.min(...nums), Math.max(...nums)];\n }, [values]);\n\n const shapes = useMemo(() => {\n if (!collection) return null;\n const projection = fitProjection(\n geometriesBounds(collection.features.map((f) => f.geometry)),\n width,\n height,\n { padding },\n );\n return collection.features.map((feature) => ({\n id: feature.properties.id,\n name: feature.properties.name,\n d: geometryPath(feature.geometry, projection),\n centroid: geometryCentroid(feature.geometry, projection),\n }));\n }, [collection, width, height, padding]);\n\n function fillFor(id: string, name: string): string | undefined {\n if (!values || !valueRange) return undefined;\n const v = values[id] ?? values[name];\n if (typeof v !== \"number\") return undefined;\n const [min, max] = valueRange;\n const t = max === min ? 1 : (v - min) / (max - min);\n return lerpColor(minColor, maxColor, t);\n }\n\n const resolvedLabel = label ?? `Municípios de ${getState(uf)?.name ?? uf}`;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"group\"\n aria-label={resolvedLabel}\n {...rest}\n >\n {!shapes ? (\n <div className={styles.loading}>{loadingContent ?? \"Carregando municípios…\"}</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {shapes.map((shape) => {\n const isSelected = selectedSet.has(shape.id) || selectedSet.has(shape.name);\n return (\n <path\n key={shape.id}\n className={cn(styles.state, isSelected && styles.selected)}\n d={shape.d}\n fillRule=\"evenodd\"\n fill={isSelected ? undefined : fillFor(shape.id, shape.name)}\n data-city-id={shape.id}\n data-city={shape.name}\n tabIndex={onSelect ? 0 : undefined}\n role={onSelect ? \"button\" : undefined}\n aria-label={shape.name}\n aria-pressed={onSelect ? isSelected : undefined}\n onClick={\n onSelect\n ? () => onSelect({ id: shape.id, name: shape.name })\n : undefined\n }\n onKeyDown={\n onSelect\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect({ id: shape.id, name: shape.name });\n }\n }\n : undefined\n }\n >\n <title>{shape.name}</title>\n </path>\n );\n })}\n\n {showLabels &&\n shapes.map((shape) => (\n <text\n key={`label-${shape.id}`}\n className={styles.label}\n x={shape.centroid.x}\n y={shape.centroid.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n >\n {shape.name}\n </text>\n ))}\n </svg>\n )}\n </div>\n );\n}\n","import { useMemo, useState, type ReactElement } from \"react\";\nimport { Select } from \"@/components/Select\";\nimport { cityChoices, listStates, normalizeUf, type UF } 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\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 */\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 ? cityChoices(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 });\n }\n\n function handleCity(next: string): void {\n const nextCity = next || null;\n setCity(nextCity);\n onChange?.({ uf, city: nextCity });\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","// Brazilian locations data (states + cities) — mirrors the `utils/locations`\n// module of tempest-fastapi-sdk.\nexport {\n listStates,\n getState,\n citiesByUf,\n statesByRegion,\n isValidUf,\n normalizeUf,\n isValidCity,\n ufChoices,\n cityChoices,\n} from \"./locations\";\nexport type { UF, BrRegion, BrazilState, Choice } from \"./locations\";\n\n// UF GeoJSON types (the geometry itself is lazy-loaded by BrazilMap).\nimport type { BrUfFeatureCollection } from \"./br-geo\";\nexport type { BrUfFeature, BrUfFeatureCollection, BrUfGeometry, Ring } from \"./br-geo\";\n\n/**\n * Lazily load the bundled simplified UF GeoJSON (~36 KB gzip). Kept out of the\n * synchronous barrel so a data-only import never pulls the geometry.\n */\nexport async function loadBrUfGeoJson(): Promise<BrUfFeatureCollection> {\n const mod = await import(\"./br-geo\");\n return mod.BR_UF_GEOJSON;\n}\n\n// Per-state municipality geometry (lazy, one chunk per UF).\nexport { loadStateMunicipalities } from \"./state-geo\";\nexport type { MunicipalityFeature, StateMunicipalities } from \"./state-geo\";\n\n// Components\nexport { BrazilMap } from \"./BrazilMap\";\nexport type { BrazilMapProps } from \"./BrazilMap\";\nexport { BrazilStateMap } from \"./BrazilStateMap\";\nexport type { BrazilStateMapProps, Municipality } from \"./BrazilStateMap\";\nexport { BrazilStateCitySelect } from \"./BrazilStateCitySelect\";\nexport type { BrazilStateCitySelectProps, BrazilStateCitySelection } from \"./BrazilStateCitySelect\";\n"],"names":["REGION_BY_UF","RAW","rawLocations","STATES","entry","uf","b","STATE_BY_UF","s","listStates","getState","normalized","normalizeUf","citiesByUf","statesByRegion","region","isValidUf","value","upper","isValidCity","city","target","c","ufChoices","cityChoices","LOADERS","loadStateMunicipalities","loader","mod","ringsOf","geometry","geometriesBounds","geometries","minLat","maxLat","minLon","maxLon","ring","lon","lat","geometryPath","projection","p","geometryCentroid","rings","outer","a","sx","sy","n","lerpColor","from","to","t","parse","hex","full","ch","r1","g1","b1","r2","g2","b2","mix","BrazilMap","selected","onSelect","values","minColor","maxColor","height","padding","showLabels","label","loadingContent","className","style","rest","containerRef","useRef","width","setWidth","useState","collection","setCollection","useEffect","active","m","node","observer","entries","measured","selectedSet","useMemo","valueRange","nums","v","shapes","fitProjection","f","feature","fillFor","min","max","jsx","cn","styles","jsxs","shape","isSelected","e","BrazilStateMap","data","id","name","resolvedLabel","BrazilStateCitySelect","defaultUf","defaultCity","onChange","stateLabel","cityLabel","statePlaceholder","cityPlaceholder","disabled","layout","setUf","setCity","stateOptions","cityOptions","handleUf","next","nextUf","handleCity","nextCity","Select","loadBrUfGeoJson"],"mappings":"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"}
1
+ {"version":3,"file":"br.cjs","sources":["../src/br/locations.ts","../src/br/state-geo.ts","../src/br/MapTooltip.tsx","../src/br/svg-utils.ts","../src/br/use-map-hover.ts","../src/br/BrazilMap.tsx","../src/br/BrazilStateMap.tsx","../src/br/BrazilStateCitySelect.tsx","../src/br/index.ts"],"sourcesContent":["import rawLocations from \"./data/br-locations.json\";\n\n/** The 27 Brazilian federative units (26 states + Federal District). */\nexport type UF =\n | \"AC\"\n | \"AL\"\n | \"AP\"\n | \"AM\"\n | \"BA\"\n | \"CE\"\n | \"DF\"\n | \"ES\"\n | \"GO\"\n | \"MA\"\n | \"MT\"\n | \"MS\"\n | \"MG\"\n | \"PA\"\n | \"PB\"\n | \"PR\"\n | \"PE\"\n | \"PI\"\n | \"RJ\"\n | \"RN\"\n | \"RS\"\n | \"RO\"\n | \"RR\"\n | \"SC\"\n | \"SP\"\n | \"SE\"\n | \"TO\";\n\n/** The five Brazilian macro-regions (IBGE). */\nexport type BrRegion = \"Norte\" | \"Nordeste\" | \"Centro-Oeste\" | \"Sudeste\" | \"Sul\";\n\n/** A federative unit with its display name and city list. */\nexport interface BrazilState {\n /** Two-letter acronym, e.g. `\"SP\"`. */\n uf: UF;\n /** Full name, e.g. `\"São Paulo\"`. */\n name: string;\n /** Macro-region the state belongs to. */\n region: BrRegion;\n /** City names within the state, alphabetically. */\n cities: string[];\n}\n\n/** A `{ value, label }` option, handy for `<Select>` / `<Combobox>`. */\nexport interface Choice {\n value: string;\n label: string;\n}\n\n/** IBGE macro-region of each federative unit. */\nconst REGION_BY_UF: Record<UF, BrRegion> = {\n AC: \"Norte\",\n AP: \"Norte\",\n AM: \"Norte\",\n PA: \"Norte\",\n RO: \"Norte\",\n RR: \"Norte\",\n TO: \"Norte\",\n AL: \"Nordeste\",\n BA: \"Nordeste\",\n CE: \"Nordeste\",\n MA: \"Nordeste\",\n PB: \"Nordeste\",\n PE: \"Nordeste\",\n PI: \"Nordeste\",\n RN: \"Nordeste\",\n SE: \"Nordeste\",\n DF: \"Centro-Oeste\",\n GO: \"Centro-Oeste\",\n MT: \"Centro-Oeste\",\n MS: \"Centro-Oeste\",\n ES: \"Sudeste\",\n MG: \"Sudeste\",\n RJ: \"Sudeste\",\n SP: \"Sudeste\",\n PR: \"Sul\",\n RS: \"Sul\",\n SC: \"Sul\",\n};\n\ninterface RawState {\n sigla: string;\n estado: string;\n cidades: string[];\n}\n\nconst RAW = rawLocations as unknown as { dataLocals: RawState[] };\n\n/** Normalized, frozen list of all 27 states (built once at module load). */\nconst STATES: readonly BrazilState[] = RAW.dataLocals\n .map((entry) => {\n const uf = entry.sigla as UF;\n return {\n uf,\n name: entry.estado,\n region: REGION_BY_UF[uf],\n cities: entry.cidades,\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name, \"pt-BR\"));\n\nconst STATE_BY_UF = new Map<UF, BrazilState>(STATES.map((s) => [s.uf, s]));\n\n/** All 27 federative units, sorted by name. */\nexport function listStates(): readonly BrazilState[] {\n return STATES;\n}\n\n/** Look up a single state by acronym (case-insensitive). Returns `null` if unknown. */\nexport function getState(uf: string): BrazilState | null {\n const normalized = normalizeUf(uf);\n return normalized ? (STATE_BY_UF.get(normalized) ?? null) : null;\n}\n\n/**\n * City names for a federative unit (case-insensitive acronym). Returns an empty\n * array for an unknown UF — \"no matches\" is a valid result, not an error.\n */\nexport function citiesByUf(uf: string): string[] {\n return getState(uf)?.cities ?? [];\n}\n\n/** States belonging to a macro-region. */\nexport function statesByRegion(region: BrRegion): readonly BrazilState[] {\n return STATES.filter((s) => s.region === region);\n}\n\n/** True when `value` is one of the 27 valid acronyms (case-insensitive). */\nexport function isValidUf(value: string): boolean {\n return normalizeUf(value) !== null;\n}\n\n/**\n * Normalize an acronym to canonical uppercase form, or `null` if it is not a\n * valid UF. `\"sp\"` → `\"SP\"`, `\"xx\"` → `null`.\n */\nexport function normalizeUf(value: string): UF | null {\n const upper = value.trim().toUpperCase();\n return (REGION_BY_UF as Record<string, BrRegion>)[upper] ? (upper as UF) : null;\n}\n\n/** True when `city` exists within `uf` (both case-insensitive). */\nexport function isValidCity(uf: string, city: string): boolean {\n const target = city.trim().toLowerCase();\n return citiesByUf(uf).some((c) => c.toLowerCase() === target);\n}\n\n/** `{ value: uf, label: name }` options for every state, for a `<Select>`. */\nexport function ufChoices(): Choice[] {\n return STATES.map((s) => ({ value: s.uf, label: s.name }));\n}\n\n/** `{ value, label }` options for every city in a UF (value === label). */\nexport function cityChoices(uf: string): Choice[] {\n return citiesByUf(uf).map((c) => ({ value: c, label: c }));\n}\n","import type { BrUfGeometry } from \"./br-geo\";\nimport type { UF } from \"./locations\";\n\n/** One municipality feature within a state. */\nexport interface MunicipalityFeature {\n type: \"Feature\";\n properties: {\n /** 7-digit IBGE municipality code. */\n id: string;\n /** Municipality name. */\n name: string;\n };\n geometry: BrUfGeometry;\n}\n\n/** Municipality boundaries for a single federative unit. */\nexport interface StateMunicipalities {\n type: \"FeatureCollection\";\n /** The federative unit these municipalities belong to. */\n uf: UF;\n features: MunicipalityFeature[];\n}\n\ntype GeoModule = { default: StateMunicipalities };\n\n// One explicit dynamic import per UF — each `data/mun/<UF>.json` becomes its own\n// lazy chunk, so `loadStateMunicipalities(\"SP\")` fetches only São Paulo's\n// geometry and the full ~2 MB municipal dataset never lands in one bundle.\nconst LOADERS: Record<UF, () => Promise<GeoModule>> = {\n AC: () => import(\"./data/mun/AC.json\") as unknown as Promise<GeoModule>,\n AL: () => import(\"./data/mun/AL.json\") as unknown as Promise<GeoModule>,\n AP: () => import(\"./data/mun/AP.json\") as unknown as Promise<GeoModule>,\n AM: () => import(\"./data/mun/AM.json\") as unknown as Promise<GeoModule>,\n BA: () => import(\"./data/mun/BA.json\") as unknown as Promise<GeoModule>,\n CE: () => import(\"./data/mun/CE.json\") as unknown as Promise<GeoModule>,\n DF: () => import(\"./data/mun/DF.json\") as unknown as Promise<GeoModule>,\n ES: () => import(\"./data/mun/ES.json\") as unknown as Promise<GeoModule>,\n GO: () => import(\"./data/mun/GO.json\") as unknown as Promise<GeoModule>,\n MA: () => import(\"./data/mun/MA.json\") as unknown as Promise<GeoModule>,\n MT: () => import(\"./data/mun/MT.json\") as unknown as Promise<GeoModule>,\n MS: () => import(\"./data/mun/MS.json\") as unknown as Promise<GeoModule>,\n MG: () => import(\"./data/mun/MG.json\") as unknown as Promise<GeoModule>,\n PA: () => import(\"./data/mun/PA.json\") as unknown as Promise<GeoModule>,\n PB: () => import(\"./data/mun/PB.json\") as unknown as Promise<GeoModule>,\n PR: () => import(\"./data/mun/PR.json\") as unknown as Promise<GeoModule>,\n PE: () => import(\"./data/mun/PE.json\") as unknown as Promise<GeoModule>,\n PI: () => import(\"./data/mun/PI.json\") as unknown as Promise<GeoModule>,\n RJ: () => import(\"./data/mun/RJ.json\") as unknown as Promise<GeoModule>,\n RN: () => import(\"./data/mun/RN.json\") as unknown as Promise<GeoModule>,\n RS: () => import(\"./data/mun/RS.json\") as unknown as Promise<GeoModule>,\n RO: () => import(\"./data/mun/RO.json\") as unknown as Promise<GeoModule>,\n RR: () => import(\"./data/mun/RR.json\") as unknown as Promise<GeoModule>,\n SC: () => import(\"./data/mun/SC.json\") as unknown as Promise<GeoModule>,\n SP: () => import(\"./data/mun/SP.json\") as unknown as Promise<GeoModule>,\n SE: () => import(\"./data/mun/SE.json\") as unknown as Promise<GeoModule>,\n TO: () => import(\"./data/mun/TO.json\") as unknown as Promise<GeoModule>,\n};\n\n/**\n * Lazily load the simplified municipality GeoJSON for a federative unit. Each\n * state's geometry is a separate chunk (~40-70 KB gzip), fetched on demand —\n * the full ~2 MB municipal dataset never lands in one bundle.\n *\n * @param uf - Federative unit acronym (e.g. `\"SP\"`).\n * @returns The municipality collection, or `null` for an unknown UF.\n *\n * @example\n * const sp = await loadStateMunicipalities(\"SP\");\n * sp?.features.length; // 644\n */\nexport async function loadStateMunicipalities(uf: UF): Promise<StateMunicipalities | null> {\n const loader = LOADERS[uf];\n if (!loader) return null;\n const mod = await loader();\n return mod.default ?? (mod as unknown as StateMunicipalities);\n}\n","import type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./BrazilMap.module.css\";\n\nexport interface MapTooltipProps {\n /** X position in pixels, relative to the map container. */\n x: number;\n /** Y position in pixels, relative to the map container. */\n y: number;\n /** Tooltip body. */\n children: ReactNode;\n className?: string;\n}\n\n/**\n * Floating tooltip anchored near the cursor over a map. Positioned absolutely\n * inside the map container (which is `position: relative`) and non-interactive\n * so it never steals hover from the shapes beneath it.\n */\nexport function MapTooltip({ x, y, children, className }: MapTooltipProps) {\n return (\n <div\n className={cn(styles.tooltip, className)}\n style={{ left: x, top: y }}\n data-testid=\"map-tooltip\"\n aria-hidden\n >\n {children}\n </div>\n );\n}\n","import type { FittedProjection } from \"@/geo/projection\";\nimport type { GeoBounds } from \"@/geo/types\";\nimport type { BrUfGeometry, Ring } from \"./br-geo\";\n\n/** Iterate every ring of a geometry (Polygon or MultiPolygon). */\nexport function ringsOf(geometry: BrUfGeometry): Ring[] {\n return geometry.type === \"MultiPolygon\"\n ? (geometry.coordinates as Ring[][]).flat()\n : (geometry.coordinates as Ring[]);\n}\n\n/** Bounding box across every coordinate of the given geometries. */\nexport function geometriesBounds(geometries: readonly BrUfGeometry[]): GeoBounds {\n let minLat = 90;\n let maxLat = -90;\n let minLon = 180;\n let maxLon = -180;\n for (const geometry of geometries) {\n for (const ring of ringsOf(geometry)) {\n for (const [lon, lat] of ring) {\n if (lat < minLat) minLat = lat;\n if (lat > maxLat) maxLat = lat;\n if (lon < minLon) minLon = lon;\n if (lon > maxLon) maxLon = lon;\n }\n }\n }\n return { minLatitude: minLat, maxLatitude: maxLat, minLongitude: minLon, maxLongitude: maxLon };\n}\n\n/** Build the SVG `d` path for a geometry (all rings, `evenodd` handles holes). */\nexport function geometryPath(geometry: BrUfGeometry, projection: FittedProjection): string {\n return ringsOf(geometry)\n .map((ring) => {\n const pts = ring.map(([lon, lat]) => {\n const p = projection.project({ latitude: lat, longitude: lon });\n return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;\n });\n return `M${pts.join(\"L\")}Z`;\n })\n .join(\"\");\n}\n\n/** Rough visual centroid of a geometry (mean of its largest ring's points). */\nexport function geometryCentroid(\n geometry: BrUfGeometry,\n projection: FittedProjection,\n): { x: number; y: number } {\n const rings = ringsOf(geometry);\n const outer = rings.reduce((a, b) => (b.length > a.length ? b : a), rings[0] ?? []);\n let sx = 0;\n let sy = 0;\n for (const [lon, lat] of outer) {\n const p = projection.project({ latitude: lat, longitude: lon });\n sx += p.x;\n sy += p.y;\n }\n const n = outer.length || 1;\n return { x: sx / n, y: sy / n };\n}\n\n/** Linearly interpolate between two hex colors, returning an `rgb(...)` string. */\nexport function lerpColor(from: string, to: string, t: number): string {\n const parse = (c: string): [number, number, number] => {\n const hex = c.replace(\"#\", \"\");\n const full =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((ch) => ch + ch)\n .join(\"\")\n : hex;\n return [\n parseInt(full.slice(0, 2), 16),\n parseInt(full.slice(2, 4), 16),\n parseInt(full.slice(4, 6), 16),\n ];\n };\n const [r1, g1, b1] = parse(from);\n const [r2, g2, b2] = parse(to);\n const mix = (a: number, b: number): number => Math.round(a + (b - a) * t);\n return `rgb(${mix(r1, r2)}, ${mix(g1, g2)}, ${mix(b1, b2)})`;\n}\n","import { useCallback, useState, type MouseEvent, type RefObject } from \"react\";\n\n/** The currently hovered item plus cursor position within the map container. */\nexport interface MapHover<T> {\n item: T;\n x: number;\n y: number;\n}\n\n/**\n * Track which shape the cursor is over and where, for a floating map tooltip.\n * Positions are computed relative to `containerRef` so the tooltip can be\n * placed with plain `left`/`top` inside the (relatively positioned) container.\n */\nexport function useMapHover<T>(containerRef: RefObject<HTMLDivElement | null>): {\n hover: MapHover<T> | null;\n onMove: (item: T, event: MouseEvent) => void;\n onLeave: () => void;\n} {\n const [hover, setHover] = useState<MapHover<T> | null>(null);\n\n const onMove = useCallback(\n (item: T, event: MouseEvent): void => {\n const rect = containerRef.current?.getBoundingClientRect();\n if (!rect) return;\n setHover({ item, x: event.clientX - rect.left, y: event.clientY - rect.top });\n },\n [containerRef],\n );\n\n const onLeave = useCallback((): void => setHover(null), []);\n\n return { hover, onMove, onLeave };\n}\n","import { useEffect, useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { fitProjection } from \"@/geo/projection\";\nimport type { BrUfFeatureCollection } from \"./br-geo\";\nimport { getState, type UF } from \"./locations\";\nimport { MapTooltip } from \"./MapTooltip\";\nimport { geometriesBounds, geometryCentroid, geometryPath, lerpColor } from \"./svg-utils\";\nimport { useMapHover } from \"./use-map-hover\";\nimport styles from \"./BrazilMap.module.css\";\n\n/** Data passed to a {@link BrazilMapProps.renderTooltip} callback. */\nexport interface BrazilMapTooltipData {\n uf: UF;\n name: string;\n /** Choropleth value for this UF, if `values` was provided. */\n value?: number;\n}\n\nexport interface BrazilMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** Currently selected UF(s) — highlighted. Accepts one or many. */\n selected?: UF | readonly UF[] | null;\n /** Fired when a state is clicked. */\n onSelect?: (uf: UF) => void;\n /**\n * Optional choropleth values per UF. When set, each state is tinted between\n * `minColor` and `maxColor` by its value (linear). States without a value\n * use the base surface color.\n */\n values?: Partial<Record<UF, number>>;\n /** Choropleth low-end color. Default: a light primary tint. */\n minColor?: string;\n /** Choropleth high-end color. Default: the primary token. */\n maxColor?: string;\n /** Viewport height in pixels. Default: `440`. */\n height?: number;\n /** Inner padding in pixels. Default: `12`. */\n padding?: number;\n /** Render the UF acronym at each state centroid. Default: `true`. */\n showLabels?: boolean;\n /** Accessible label for the map region. Default: `\"Mapa do Brasil por estado\"`. */\n label?: string;\n /** Custom content when the geometry is still loading. */\n loadingContent?: ReactNode;\n /** Show a floating tooltip (name + region + city count + value) on hover. Default: `true`. */\n showTooltip?: boolean;\n /** Override the default tooltip content. */\n renderTooltip?: (data: BrazilMapTooltipData) => ReactNode;\n}\n\n/**\n * Clickable choropleth map of Brazil's 27 federative units. Renders the bundled\n * simplified UF GeoJSON as SVG paths — **no external tiles or paid API**. Click\n * a state to fire `onSelect(uf)`; pass `selected` to highlight and `values` to\n * tint states by a metric.\n *\n * The GeoJSON (~36 KB gzip) is loaded lazily, so importing this component does\n * not pull the geometry until it actually mounts.\n *\n * @example\n * const [uf, setUf] = useState<UF | null>(null);\n * <BrazilMap selected={uf} onSelect={setUf} />\n *\n * @example\n * // Choropleth by a metric per state\n * <BrazilMap values={{ SP: 120, RJ: 90, MG: 60 }} />\n */\nexport function BrazilMap({\n selected,\n onSelect,\n values,\n minColor = \"#dbeafe\",\n maxColor = \"#2563eb\",\n height = 440,\n padding = 12,\n showLabels = true,\n label = \"Mapa do Brasil por estado\",\n loadingContent,\n showTooltip = true,\n renderTooltip,\n className,\n style,\n ...rest\n}: BrazilMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n const [collection, setCollection] = useState<BrUfFeatureCollection | null>(null);\n const { hover, onMove, onLeave } = useMapHover<{ uf: UF; name: string }>(containerRef);\n\n useEffect(() => {\n let active = true;\n void import(\"./br-geo\").then((m) => {\n if (active) setCollection(m.BR_UF_GEOJSON);\n });\n return () => {\n active = false;\n };\n }, []);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const selectedSet = useMemo<Set<UF>>(() => {\n if (!selected) return new Set();\n return new Set(Array.isArray(selected) ? selected : [selected as UF]);\n }, [selected]);\n\n const valueRange = useMemo<[number, number] | null>(() => {\n if (!values) return null;\n const nums = Object.values(values).filter((v): v is number => typeof v === \"number\");\n if (nums.length === 0) return null;\n return [Math.min(...nums), Math.max(...nums)];\n }, [values]);\n\n const shapes = useMemo(() => {\n if (!collection) return null;\n const projection = fitProjection(\n geometriesBounds(collection.features.map((f) => f.geometry)),\n width,\n height,\n { padding },\n );\n return collection.features.map((feature) => ({\n uf: feature.properties.uf,\n name: feature.properties.name,\n d: geometryPath(feature.geometry, projection),\n centroid: geometryCentroid(feature.geometry, projection),\n }));\n }, [collection, width, height, padding]);\n\n function fillFor(uf: UF): string | undefined {\n if (!values || !valueRange) return undefined;\n const v = values[uf];\n if (typeof v !== \"number\") return undefined;\n const [min, max] = valueRange;\n const t = max === min ? 1 : (v - min) / (max - min);\n return lerpColor(minColor, maxColor, t);\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"group\"\n aria-label={label}\n {...rest}\n >\n {!shapes ? (\n <div className={styles.loading}>{loadingContent ?? \"Carregando mapa…\"}</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {shapes.map((shape) => {\n const isSelected = selectedSet.has(shape.uf);\n return (\n <path\n key={shape.uf}\n className={cn(styles.state, isSelected && styles.selected)}\n d={shape.d}\n fillRule=\"evenodd\"\n fill={isSelected ? undefined : fillFor(shape.uf)}\n data-uf={shape.uf}\n tabIndex={onSelect ? 0 : undefined}\n role={onSelect ? \"button\" : undefined}\n aria-label={shape.name}\n aria-pressed={onSelect ? isSelected : undefined}\n onClick={onSelect ? () => onSelect(shape.uf) : undefined}\n onMouseMove={\n showTooltip\n ? (e) => onMove({ uf: shape.uf, name: shape.name }, e)\n : undefined\n }\n onMouseLeave={showTooltip ? onLeave : undefined}\n onKeyDown={\n onSelect\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect(shape.uf);\n }\n }\n : undefined\n }\n />\n );\n })}\n\n {showLabels &&\n shapes.map((shape) => (\n <text\n key={`label-${shape.uf}`}\n className={styles.label}\n x={shape.centroid.x}\n y={shape.centroid.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n >\n {shape.uf}\n </text>\n ))}\n </svg>\n )}\n\n {showTooltip && hover && (\n <MapTooltip x={hover.x} y={hover.y}>\n {renderTooltip ? (\n renderTooltip({\n uf: hover.item.uf,\n name: hover.item.name,\n value: values?.[hover.item.uf],\n })\n ) : (\n <DefaultUfTooltip\n uf={hover.item.uf}\n name={hover.item.name}\n value={values?.[hover.item.uf]}\n />\n )}\n </MapTooltip>\n )}\n </div>\n );\n}\n\n/** Default UF tooltip: name (UF) + region + city count + optional value. */\nfunction DefaultUfTooltip({ uf, name, value }: { uf: UF; name: string; value?: number }) {\n const state = getState(uf);\n return (\n <>\n <div className={styles.tooltipTitle}>\n {name} ({uf})\n </div>\n <div className={styles.tooltipMeta}>\n {state ? `${state.region} · ${state.cities.length} cidades` : uf}\n {typeof value === \"number\" ? ` · ${value}` : \"\"}\n </div>\n </>\n );\n}\n","import { useEffect, useMemo, useRef, useState, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { fitProjection } from \"@/geo/projection\";\nimport { getState, type UF } from \"./locations\";\nimport { MapTooltip } from \"./MapTooltip\";\nimport { loadStateMunicipalities, type StateMunicipalities } from \"./state-geo\";\nimport { geometriesBounds, geometryCentroid, geometryPath, lerpColor } from \"./svg-utils\";\nimport { useMapHover } from \"./use-map-hover\";\nimport styles from \"./BrazilMap.module.css\";\n\n/** A municipality identified for selection callbacks. */\nexport interface Municipality {\n /** 7-digit IBGE code. */\n id: string;\n /** Municipality name. */\n name: string;\n}\n\n/** Data passed to a {@link BrazilStateMapProps.renderTooltip} callback. */\nexport interface BrazilStateMapTooltipData extends Municipality {\n /** Choropleth value for this municipality, if `values` was provided. */\n value?: number;\n}\n\nexport interface BrazilStateMapProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** Federative unit to draw (required). */\n uf: UF;\n /** Selected municipality — matched by IBGE `id` or by `name`. Accepts many. */\n selected?: string | readonly string[] | null;\n /** Fired when a municipality is clicked. */\n onSelect?: (municipality: Municipality) => void;\n /**\n * Choropleth values keyed by municipality `id` **or** `name`. When set, each\n * municipality is tinted between `minColor` and `maxColor`.\n */\n values?: Record<string, number>;\n /** Choropleth low-end color. Default: a light primary tint. */\n minColor?: string;\n /** Choropleth high-end color. Default: the primary token. */\n maxColor?: string;\n /** Viewport height in pixels. Default: `440`. */\n height?: number;\n /** Inner padding in pixels. Default: `12`. */\n padding?: number;\n /**\n * Render each municipality name at its centroid. Off by default — a state\n * can have hundreds of municipalities and labels overlap badly.\n */\n showLabels?: boolean;\n /** Accessible label. Default: derived from the state name. */\n label?: string;\n /** Content while the state geometry is loading. */\n loadingContent?: ReactNode;\n /** Show a floating tooltip (name + IBGE code + value) on hover. Default: `true`. */\n showTooltip?: boolean;\n /** Override the default tooltip content. */\n renderTooltip?: (data: BrazilStateMapTooltipData) => ReactNode;\n}\n\n/**\n * Clickable submap of a single Brazilian state showing **all its\n * municipalities** as SVG paths — no external tiles or paid API. The state's\n * geometry (~40-70 KB gzip) is loaded lazily per UF, so switching states fetches\n * only what is shown.\n *\n * @example\n * const [city, setCity] = useState<string | null>(null);\n * <BrazilStateMap uf=\"SP\" selected={city} onSelect={(m) => setCity(m.name)} />\n *\n * @example\n * // Choropleth of a metric by municipality name\n * <BrazilStateMap uf=\"RJ\" values={{ \"Rio de Janeiro\": 100, Niterói: 40 }} />\n */\nexport function BrazilStateMap({\n uf,\n selected,\n onSelect,\n values,\n minColor = \"#dbeafe\",\n maxColor = \"#2563eb\",\n height = 440,\n padding = 12,\n showLabels = false,\n label,\n loadingContent,\n showTooltip = true,\n renderTooltip,\n className,\n style,\n ...rest\n}: BrazilStateMapProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState<number>(600);\n const [collection, setCollection] = useState<StateMunicipalities | null>(null);\n const { hover, onMove, onLeave } = useMapHover<Municipality>(containerRef);\n\n useEffect(() => {\n let active = true;\n setCollection(null);\n void loadStateMunicipalities(uf).then((data) => {\n if (active) setCollection(data);\n });\n return () => {\n active = false;\n };\n }, [uf]);\n\n useEffect(() => {\n const node = containerRef.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const measured = entries[0]?.contentRect.width;\n if (measured && measured > 0) setWidth(measured);\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const selectedSet = useMemo<Set<string>>(() => {\n if (!selected) return new Set();\n return new Set(Array.isArray(selected) ? selected : [selected as string]);\n }, [selected]);\n\n const valueRange = useMemo<[number, number] | null>(() => {\n if (!values) return null;\n const nums = Object.values(values).filter((v): v is number => typeof v === \"number\");\n if (nums.length === 0) return null;\n return [Math.min(...nums), Math.max(...nums)];\n }, [values]);\n\n const shapes = useMemo(() => {\n if (!collection) return null;\n const projection = fitProjection(\n geometriesBounds(collection.features.map((f) => f.geometry)),\n width,\n height,\n { padding },\n );\n return collection.features.map((feature) => ({\n id: feature.properties.id,\n name: feature.properties.name,\n d: geometryPath(feature.geometry, projection),\n centroid: geometryCentroid(feature.geometry, projection),\n }));\n }, [collection, width, height, padding]);\n\n function fillFor(id: string, name: string): string | undefined {\n if (!values || !valueRange) return undefined;\n const v = values[id] ?? values[name];\n if (typeof v !== \"number\") return undefined;\n const [min, max] = valueRange;\n const t = max === min ? 1 : (v - min) / (max - min);\n return lerpColor(minColor, maxColor, t);\n }\n\n const resolvedLabel = label ?? `Municípios de ${getState(uf)?.name ?? uf}`;\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.map, className)}\n style={{ height, ...style }}\n role=\"group\"\n aria-label={resolvedLabel}\n {...rest}\n >\n {!shapes ? (\n <div className={styles.loading}>{loadingContent ?? \"Carregando municípios…\"}</div>\n ) : (\n <svg\n className={styles.svg}\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"xMidYMid meet\"\n >\n {shapes.map((shape) => {\n const isSelected = selectedSet.has(shape.id) || selectedSet.has(shape.name);\n return (\n <path\n key={shape.id}\n className={cn(styles.state, isSelected && styles.selected)}\n d={shape.d}\n fillRule=\"evenodd\"\n fill={isSelected ? undefined : fillFor(shape.id, shape.name)}\n data-city-id={shape.id}\n data-city={shape.name}\n tabIndex={onSelect ? 0 : undefined}\n role={onSelect ? \"button\" : undefined}\n aria-label={shape.name}\n aria-pressed={onSelect ? isSelected : undefined}\n onClick={\n onSelect\n ? () => onSelect({ id: shape.id, name: shape.name })\n : undefined\n }\n onMouseMove={\n showTooltip\n ? (e) => onMove({ id: shape.id, name: shape.name }, e)\n : undefined\n }\n onMouseLeave={showTooltip ? onLeave : undefined}\n onKeyDown={\n onSelect\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect({ id: shape.id, name: shape.name });\n }\n }\n : undefined\n }\n />\n );\n })}\n\n {showLabels &&\n shapes.map((shape) => (\n <text\n key={`label-${shape.id}`}\n className={styles.label}\n x={shape.centroid.x}\n y={shape.centroid.y}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n >\n {shape.name}\n </text>\n ))}\n </svg>\n )}\n\n {showTooltip && hover && (\n <MapTooltip x={hover.x} y={hover.y}>\n {renderTooltip ? (\n renderTooltip({\n ...hover.item,\n value: values?.[hover.item.id] ?? values?.[hover.item.name],\n })\n ) : (\n <>\n <div className={styles.tooltipTitle}>{hover.item.name}</div>\n <div className={styles.tooltipMeta}>\n IBGE {hover.item.id}\n {(() => {\n const v = values?.[hover.item.id] ?? values?.[hover.item.name];\n return typeof v === \"number\" ? ` · ${v}` : \"\";\n })()}\n </div>\n </>\n )}\n </MapTooltip>\n )}\n </div>\n );\n}\n","import { useMemo, useState, type ReactElement } from \"react\";\nimport { Select } from \"@/components/Select\";\nimport { cityChoices, listStates, normalizeUf, type UF } 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\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 */\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 ? cityChoices(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 });\n }\n\n function handleCity(next: string): void {\n const nextCity = next || null;\n setCity(nextCity);\n onChange?.({ uf, city: nextCity });\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","// Brazilian locations data (states + cities) — mirrors the `utils/locations`\n// module of tempest-fastapi-sdk.\nexport {\n listStates,\n getState,\n citiesByUf,\n statesByRegion,\n isValidUf,\n normalizeUf,\n isValidCity,\n ufChoices,\n cityChoices,\n} from \"./locations\";\nexport type { UF, BrRegion, BrazilState, Choice } from \"./locations\";\n\n// UF GeoJSON types (the geometry itself is lazy-loaded by BrazilMap).\nimport type { BrUfFeatureCollection } from \"./br-geo\";\nexport type { BrUfFeature, BrUfFeatureCollection, BrUfGeometry, Ring } from \"./br-geo\";\n\n/**\n * Lazily load the bundled simplified UF GeoJSON (~36 KB gzip). Kept out of the\n * synchronous barrel so a data-only import never pulls the geometry.\n */\nexport async function loadBrUfGeoJson(): Promise<BrUfFeatureCollection> {\n const mod = await import(\"./br-geo\");\n return mod.BR_UF_GEOJSON;\n}\n\n// Per-state municipality geometry (lazy, one chunk per UF).\nexport { loadStateMunicipalities } from \"./state-geo\";\nexport type { MunicipalityFeature, StateMunicipalities } from \"./state-geo\";\n\n// Components\nexport { BrazilMap } from \"./BrazilMap\";\nexport type { BrazilMapProps, BrazilMapTooltipData } from \"./BrazilMap\";\nexport { BrazilStateMap } from \"./BrazilStateMap\";\nexport type {\n BrazilStateMapProps,\n BrazilStateMapTooltipData,\n Municipality,\n} from \"./BrazilStateMap\";\nexport { BrazilStateCitySelect } from \"./BrazilStateCitySelect\";\nexport type { BrazilStateCitySelectProps, BrazilStateCitySelection } from \"./BrazilStateCitySelect\";\n"],"names":["REGION_BY_UF","RAW","rawLocations","STATES","entry","uf","b","STATE_BY_UF","s","listStates","getState","normalized","normalizeUf","citiesByUf","statesByRegion","region","isValidUf","value","upper","isValidCity","city","target","c","ufChoices","cityChoices","LOADERS","loadStateMunicipalities","loader","mod","MapTooltip","x","y","children","className","jsx","cn","styles","ringsOf","geometry","geometriesBounds","geometries","minLat","maxLat","minLon","maxLon","ring","lon","lat","geometryPath","projection","p","geometryCentroid","rings","outer","a","sx","sy","n","lerpColor","from","to","t","parse","hex","full","ch","r1","g1","b1","r2","g2","b2","mix","useMapHover","containerRef","hover","setHover","useState","onMove","useCallback","item","event","rect","onLeave","BrazilMap","selected","onSelect","values","minColor","maxColor","height","padding","showLabels","label","loadingContent","showTooltip","renderTooltip","style","rest","useRef","width","setWidth","collection","setCollection","useEffect","active","node","observer","entries","measured","selectedSet","useMemo","valueRange","nums","v","shapes","fitProjection","f","feature","fillFor","min","max","jsxs","shape","isSelected","e","DefaultUfTooltip","name","state","Fragment","BrazilStateMap","data","id","resolvedLabel","BrazilStateCitySelect","defaultUf","defaultCity","onChange","stateLabel","cityLabel","statePlaceholder","cityPlaceholder","disabled","layout","setUf","setCity","stateOptions","cityOptions","handleUf","next","nextUf","handleCity","nextCity","Select","loadBrUfGeoJson"],"mappings":"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"}
package/dist/br.d.ts CHANGED
@@ -20,7 +20,7 @@ import { ReactNode } from 'react';
20
20
  * // Choropleth by a metric per state
21
21
  * <BrazilMap values={{ SP: 120, RJ: 90, MG: 60 }} />
22
22
  */
23
- export declare function BrazilMap({ selected, onSelect, values, minColor, maxColor, height, padding, showLabels, label, loadingContent, className, style, ...rest }: BrazilMapProps): JSX.Element;
23
+ export declare function BrazilMap({ selected, onSelect, values, minColor, maxColor, height, padding, showLabels, label, loadingContent, showTooltip, renderTooltip, className, style, ...rest }: BrazilMapProps): JSX.Element;
24
24
 
25
25
  export declare interface BrazilMapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
26
26
  /** Currently selected UF(s) — highlighted. Accepts one or many. */
@@ -47,6 +47,18 @@ export declare interface BrazilMapProps extends Omit<HTMLAttributes<HTMLDivEleme
47
47
  label?: string;
48
48
  /** Custom content when the geometry is still loading. */
49
49
  loadingContent?: ReactNode;
50
+ /** Show a floating tooltip (name + region + city count + value) on hover. Default: `true`. */
51
+ showTooltip?: boolean;
52
+ /** Override the default tooltip content. */
53
+ renderTooltip?: (data: BrazilMapTooltipData) => ReactNode;
54
+ }
55
+
56
+ /** Data passed to a {@link BrazilMapProps.renderTooltip} callback. */
57
+ export declare interface BrazilMapTooltipData {
58
+ uf: UF;
59
+ name: string;
60
+ /** Choropleth value for this UF, if `values` was provided. */
61
+ value?: number;
50
62
  }
51
63
 
52
64
  /** A federative unit with its display name and city list. */
@@ -115,7 +127,7 @@ export declare interface BrazilStateCitySelectProps {
115
127
  * // Choropleth of a metric by municipality name
116
128
  * <BrazilStateMap uf="RJ" values={{ "Rio de Janeiro": 100, Niterói: 40 }} />
117
129
  */
118
- export declare function BrazilStateMap({ uf, selected, onSelect, values, minColor, maxColor, height, padding, showLabels, label, loadingContent, className, style, ...rest }: BrazilStateMapProps): JSX.Element;
130
+ export declare function BrazilStateMap({ uf, selected, onSelect, values, minColor, maxColor, height, padding, showLabels, label, loadingContent, showTooltip, renderTooltip, className, style, ...rest }: BrazilStateMapProps): JSX.Element;
119
131
 
120
132
  export declare interface BrazilStateMapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
121
133
  /** Federative unit to draw (required). */
@@ -146,6 +158,16 @@ export declare interface BrazilStateMapProps extends Omit<HTMLAttributes<HTMLDiv
146
158
  label?: string;
147
159
  /** Content while the state geometry is loading. */
148
160
  loadingContent?: ReactNode;
161
+ /** Show a floating tooltip (name + IBGE code + value) on hover. Default: `true`. */
162
+ showTooltip?: boolean;
163
+ /** Override the default tooltip content. */
164
+ renderTooltip?: (data: BrazilStateMapTooltipData) => ReactNode;
165
+ }
166
+
167
+ /** Data passed to a {@link BrazilStateMapProps.renderTooltip} callback. */
168
+ export declare interface BrazilStateMapTooltipData extends Municipality {
169
+ /** Choropleth value for this municipality, if `values` was provided. */
170
+ value?: number;
149
171
  }
150
172
 
151
173
  /** The five Brazilian macro-regions (IBGE). */