| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | import axios from 'axios'; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | import { Suspense, useState, useEffect, useCallback, useRef } from 'react'; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | import { useSuspenseQuery } from '@tanstack/react-query'; | 
					
						
							|  |  |  | import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps'; | 
					
						
							|  |  |  | import { Tooltip } from 'react-tooltip'; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | import { geoCentroid } from 'd3-geo'; | 
					
						
							|  |  |  | import { feature } from 'topojson-client'; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | import { API_BASE_URL, assetBaseUrl } from '../../../../apiService'; | 
					
						
							|  |  |  | import type { ResultadoMapaDto, AmbitoGeography } from '../../../../types/types'; | 
					
						
							|  |  |  | import { MapaProvincial } from './MapaProvincial'; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | import { CabaLupa } from './CabaLupa'; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | import { BiZoomIn, BiZoomOut } from "react-icons/bi"; | 
					
						
							| 
									
										
										
										
											2025-10-01 10:03:01 -03:00
										 |  |  | import toast from 'react-hot-toast'; | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  | import { useMediaQuery } from '../hooks/useMediaQuery'; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | const DEFAULT_MAP_COLOR = '#E0E0E0'; | 
					
						
							|  |  |  | const FADED_BACKGROUND_COLOR = '#F0F0F0'; | 
					
						
							|  |  |  | const normalizarTexto = (texto: string = '') => texto.trim().toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type PointTuple = [number, number]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  | interface ViewConfig { | 
					
						
							|  |  |  |   center: PointTuple; | 
					
						
							|  |  |  |   zoom: number; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const PROVINCE_VIEW_CONFIG: Record<string, { desktop: ViewConfig; mobile?: ViewConfig }> = { | 
					
						
							|  |  |  |   "BUENOS AIRES": { desktop: { center: [-60.5, -37.3], zoom: 5 }, mobile: { center: [-60, -38], zoom: 5.5 } }, | 
					
						
							|  |  |  |   "SANTA CRUZ": { desktop: { center: [-69.5, -49.3], zoom: 5 }, mobile: { center: [-69.5, -50], zoom: 4 } }, | 
					
						
							|  |  |  |   "CIUDAD AUTONOMA DE BUENOS AIRES": { desktop: { center: [-58.44, -34.65], zoom: 150 } }, | 
					
						
							|  |  |  |   "CHUBUT": { desktop: { center: [-68.5, -44.5], zoom: 5.5 }, mobile: { center: [-68, -44.5], zoom: 4.5 } }, | 
					
						
							|  |  |  |   "SANTA FE": { desktop: { center: [-61, -31.2], zoom: 6 }, mobile: { center: [-61, -31.5], zoom: 7.5 } }, | 
					
						
							|  |  |  |   "CORRIENTES": { desktop: { center: [-58, -29], zoom: 7 }, mobile: { center: [-57.5, -28.8], zoom: 9 } }, | 
					
						
							|  |  |  |   "RIO NEGRO": { desktop: { center: [-67.5, -40], zoom: 5.5 }, mobile: { center: [-67.5, -40], zoom: 4.3 } }, | 
					
						
							|  |  |  |   "SALTA": { desktop: { center: [-64.5, -24], zoom: 7 }, mobile: { center: [-65.5, -24.5], zoom: 6 } }, | 
					
						
							|  |  |  |   "TIERRA DEL FUEGO": { desktop: { center: [-66.5, -54.2], zoom: 7 }, mobile: { center: [-66, -54], zoom: 7.5 } }, | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const LUPA_SIZE_RATIO = 0.2; | 
					
						
							|  |  |  | const MIN_LUPA_SIZE_PX = 100; | 
					
						
							|  |  |  | const MAX_LUPA_SIZE_PX = 180; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | interface MapaNacionalProps { | 
					
						
							|  |  |  |   eleccionId: number; | 
					
						
							|  |  |  |   categoriaId: number; | 
					
						
							|  |  |  |   nivel: 'pais' | 'provincia' | 'municipio'; | 
					
						
							|  |  |  |   nombreAmbito: string; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |   nombreProvinciaActiva: string | undefined | null; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |   provinciaDistritoId: string | null; | 
					
						
							|  |  |  |   onAmbitoSelect: (ambitoId: string, nivel: 'provincia' | 'municipio', nombre: string) => void; | 
					
						
							|  |  |  |   onVolver: () => void; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |   isMobileView: boolean; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | // --- CONFIGURACIONES DEL MAPA ---
 | 
					
						
							|  |  |  | const desktopProjectionConfig = { scale: 700, center: [-65, -40] as [number, number] }; | 
					
						
							| 
									
										
										
										
											2025-10-03 14:40:06 -03:00
										 |  |  | const mobileProjectionConfig = { scale: 1000, center: [-64, -43] as [number, number] }; | 
					
						
							|  |  |  | const mobileSmallProjectionConfig = { scale: 750, center: [-64, -45] as [number, number] }; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nombreProvinciaActiva, provinciaDistritoId, onAmbitoSelect, onVolver, isMobileView }: MapaNacionalProps) => { | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |   const isMobileSmall = useMediaQuery('(max-width: 380px)'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |   const [position, setPosition] = useState({ | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |     zoom: isMobileView ? 1.5 : 1.05, | 
					
						
							|  |  |  |     center: isMobileView ? mobileProjectionConfig.center : desktopProjectionConfig.center as PointTuple | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |   const [isPanning, setIsPanning] = useState(false); | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |   const initialProvincePositionRef = useRef<ViewConfig | null>(null); | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |   const containerRef = useRef<HTMLDivElement | null>(null); | 
					
						
							|  |  |  |   const lupaRef = useRef<HTMLDivElement | null>(null); | 
					
						
							|  |  |  |   const cabaPathRef = useRef<SVGPathElement | null>(null); | 
					
						
							|  |  |  |   const isAnimatingRef = useRef(false); | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |   const initialLoadRef = useRef(true); | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const [lupaStyle, setLupaStyle] = useState<React.CSSProperties>({ opacity: 0 }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |   const { data: mapaDataNacional } = useSuspenseQuery<ResultadoMapaDto[]>({ | 
					
						
							|  |  |  |     queryKey: ['mapaResultados', eleccionId, categoriaId, null], | 
					
						
							|  |  |  |     queryFn: async () => { | 
					
						
							|  |  |  |       const url = `${API_BASE_URL}/elecciones/${eleccionId}/mapa-resultados?categoriaId=${categoriaId}`; | 
					
						
							|  |  |  |       const response = await axios.get(url); | 
					
						
							|  |  |  |       return response.data; | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { data: geoDataNacional } = useSuspenseQuery<any>({ | 
					
						
							|  |  |  |     queryKey: ['geoDataNacional'], | 
					
						
							|  |  |  |     queryFn: () => axios.get(`${assetBaseUrl}/maps/argentina-provincias.topojson`).then(res => res.data), | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (nivel === 'pais') { | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |       const currentMobileConfig = isMobileSmall ? mobileSmallProjectionConfig : mobileProjectionConfig; | 
					
						
							|  |  |  |       const currentMobileZoom = isMobileSmall ? 1.4 : 1.5; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |       setPosition({ | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |         zoom: isMobileView ? currentMobileZoom : 1.05, | 
					
						
							|  |  |  |         center: isMobileView ? currentMobileConfig.center : desktopProjectionConfig.center | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |       }); | 
					
						
							|  |  |  |       initialProvincePositionRef.current = null; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |     } else if (nivel === 'provincia') { | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |       const nombreNormalizado = normalizarTexto(nombreAmbito); | 
					
						
							|  |  |  |       const manualConfig = PROVINCE_VIEW_CONFIG[nombreNormalizado]; | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |        | 
					
						
							|  |  |  |       let provinceConfig: ViewConfig | undefined; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |       if (manualConfig) { | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |         provinceConfig = (isMobileView && manualConfig.mobile) ? manualConfig.mobile : manualConfig.desktop; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         const provinciaGeo = geoDataNacional.objects.provincias.geometries.find((g: any) => normalizarTexto(g.properties.nombre) === nombreNormalizado); | 
					
						
							|  |  |  |         if (provinciaGeo) { | 
					
						
							|  |  |  |           const provinciaFeature = feature(geoDataNacional, provinciaGeo); | 
					
						
							|  |  |  |           const centroid = geoCentroid(provinciaFeature); | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |           provinceConfig = { zoom: isMobileView ? 8 : 7, center: centroid as PointTuple }; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |       if (provinceConfig) { | 
					
						
							|  |  |  |           setPosition(provinceConfig); | 
					
						
							|  |  |  |           initialProvincePositionRef.current = provinceConfig; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |   }, [nivel, nombreAmbito, geoDataNacional, isMobileView, isMobileSmall]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const resultadosNacionalesPorNombre = new Map<string, ResultadoMapaDto>(mapaDataNacional.map(d => [normalizarTexto(d.ambitoNombre), d])); | 
					
						
							|  |  |  |   const nombreMunicipioSeleccionado = nivel === 'municipio' ? nombreAmbito : null; | 
					
						
							|  |  |  |   const handleCalculatedCenter = useCallback((center: PointTuple, zoom: number) => { setPosition({ center, zoom }); }, []); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     const updateLupaPosition = () => { | 
					
						
							|  |  |  |       if (nivel === 'pais' && cabaPathRef.current && containerRef.current) { | 
					
						
							|  |  |  |         const containerRect = containerRef.current.getBoundingClientRect(); | 
					
						
							|  |  |  |         if (containerRect.width === 0) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const cabaRect = cabaPathRef.current.getBoundingClientRect(); | 
					
						
							|  |  |  |         const cabaCenterX = (cabaRect.left - containerRect.left) + cabaRect.width / 2; | 
					
						
							|  |  |  |         const cabaCenterY = (cabaRect.top - containerRect.top) + cabaRect.height / 2; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |         const calculatedSize = containerRect.width * LUPA_SIZE_RATIO; | 
					
						
							|  |  |  |         const newLupaSize = Math.max(MIN_LUPA_SIZE_PX, Math.min(calculatedSize, MAX_LUPA_SIZE_PX)); | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |         const horizontalOffset = newLupaSize * 0.5; | 
					
						
							|  |  |  |         const verticalOffset = newLupaSize * 0.2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setLupaStyle({ | 
					
						
							|  |  |  |           position: 'absolute', | 
					
						
							|  |  |  |           top: `${cabaCenterY - verticalOffset}px`, | 
					
						
							|  |  |  |           left: `${cabaCenterX + horizontalOffset}px`, | 
					
						
							|  |  |  |           width: `${newLupaSize}px`, | 
					
						
							|  |  |  |           opacity: 1, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         setLupaStyle({ opacity: 0, pointerEvents: 'none' }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     isAnimatingRef.current = true; | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |     const handleResize = () => { if (!isAnimatingRef.current) updateLupaPosition(); }; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |     const resizeObserver = new ResizeObserver(handleResize); | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |     if (containerRef.current) resizeObserver.observe(containerRef.current); | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |     let timerId: NodeJS.Timeout; | 
					
						
							|  |  |  |     if (initialLoadRef.current && nivel === 'pais') { | 
					
						
							|  |  |  |       timerId = setTimeout(() => { | 
					
						
							|  |  |  |         updateLupaPosition(); | 
					
						
							|  |  |  |         isAnimatingRef.current = false; | 
					
						
							|  |  |  |       }, 0); | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |       initialLoadRef.current = false; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       timerId = setTimeout(() => { | 
					
						
							|  |  |  |         updateLupaPosition(); | 
					
						
							|  |  |  |         isAnimatingRef.current = false; | 
					
						
							|  |  |  |       }, 800); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return () => { | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |       if (containerRef.current) resizeObserver.unobserve(containerRef.current); | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |       clearTimeout(timerId); | 
					
						
							|  |  |  |       isAnimatingRef.current = false; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, [position, nivel]); | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-01 10:03:01 -03:00
										 |  |  |   const panEnabled = | 
					
						
							|  |  |  |     nivel === 'provincia' && | 
					
						
							|  |  |  |     initialProvincePositionRef.current !== null && | 
					
						
							|  |  |  |     position.zoom > initialProvincePositionRef.current.zoom && | 
					
						
							|  |  |  |     !nombreMunicipioSeleccionado; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleZoomIn = () => { | 
					
						
							|  |  |  |     if (!panEnabled && initialProvincePositionRef.current) { | 
					
						
							|  |  |  |       const newZoom = position.zoom * 1.8; | 
					
						
							|  |  |  |       if (newZoom > initialProvincePositionRef.current.zoom) { | 
					
						
							|  |  |  |         toast.success('Desplazamiento Habilitado', { | 
					
						
							|  |  |  |           icon: '🖐️', | 
					
						
							|  |  |  |           style: { background: '#32e5f1ff', color: 'white' }, | 
					
						
							|  |  |  |           duration: 1000, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     setPosition(prev => ({ ...prev, zoom: Math.min(prev.zoom * 1.8, 100) })); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const handleZoomOut = () => { | 
					
						
							| 
									
										
										
										
											2025-10-01 10:03:01 -03:00
										 |  |  |     if (panEnabled && initialProvincePositionRef.current) { | 
					
						
							|  |  |  |       const newZoom = position.zoom / 1.8; | 
					
						
							|  |  |  |       if (newZoom <= initialProvincePositionRef.current.zoom) { | 
					
						
							|  |  |  |         toast.error('Desplazamiento Deshabilitado', { | 
					
						
							|  |  |  |           icon: '🔒', | 
					
						
							|  |  |  |           style: { background: '#32e5f1ff', color: 'white' }, | 
					
						
							|  |  |  |           duration: 1000, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |     setPosition(prev => { | 
					
						
							|  |  |  |       const newZoom = Math.max(prev.zoom / 1.8, 1); | 
					
						
							|  |  |  |       const initialPos = initialProvincePositionRef.current; | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |       if (initialPos && newZoom <= initialPos.zoom) return initialPos; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |       return { ...prev, zoom: newZoom }; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleMoveEnd = (newPosition: { coordinates: PointTuple, zoom: number }) => { | 
					
						
							|  |  |  |     setPosition(prev => ({ ...prev, center: newPosition.coordinates })); | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |     setIsPanning(false); | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const filterInteractionEvents = (event: any) => { | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |     if (event.sourceEvent && event.sourceEvent.type === 'wheel') return false; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |     return panEnabled; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |   const showZoomControls = nivel === 'provincia'; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |   const isZoomOutDisabled = | 
					
						
							|  |  |  |     (nivel === 'provincia' && initialProvincePositionRef.current && position.zoom <= initialProvincePositionRef.current.zoom) || | 
					
						
							|  |  |  |     (nivel === 'pais' && position.zoom <= (isMobileView ? 1.4 : 1.05)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |   const mapContainerClasses = panEnabled ? 'mapa-componente-container map-pannable' : 'mapa-componente-container map-locked'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |     <div className={mapContainerClasses} ref={containerRef}> | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |       {showZoomControls && ( | 
					
						
							|  |  |  |         <div className="zoom-controls-container"> | 
					
						
							|  |  |  |           <button onClick={handleZoomIn} className="zoom-btn" title="Acercar"> | 
					
						
							|  |  |  |             <span className="zoom-icon-wrapper"><BiZoomIn /></span> | 
					
						
							|  |  |  |           </button> | 
					
						
							|  |  |  |           <button | 
					
						
							|  |  |  |             onClick={handleZoomOut} | 
					
						
							|  |  |  |             className={`zoom-btn ${isZoomOutDisabled ? 'disabled' : ''}`} | 
					
						
							|  |  |  |             title="Alejar" | 
					
						
							|  |  |  |             disabled={isZoomOutDisabled} | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <span className="zoom-icon-wrapper"><BiZoomOut /></span> | 
					
						
							|  |  |  |           </button> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       )} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |       {nivel !== 'pais' && <button onClick={onVolver} className="mapa-volver-btn">← Volver</button>} | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       <div className="mapa-render-area"> | 
					
						
							|  |  |  |         <ComposableMap | 
					
						
							|  |  |  |           projection="geoMercator" | 
					
						
							| 
									
										
										
										
											2025-10-03 13:26:20 -03:00
										 |  |  |           projectionConfig={isMobileSmall ? mobileSmallProjectionConfig : (isMobileView ? mobileProjectionConfig : desktopProjectionConfig)} | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |           style={{ width: "100%", height: "100%" }} | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <ZoomableGroup | 
					
						
							|  |  |  |             center={position.center} | 
					
						
							|  |  |  |             zoom={position.zoom} | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |             onMoveStart={() => setIsPanning(true)} | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |             onMoveEnd={handleMoveEnd} | 
					
						
							|  |  |  |             filterZoomEvent={filterInteractionEvents} | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |             className={isPanning ? 'panning' : ''} | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |           > | 
					
						
							|  |  |  |             <Geographies geography={geoDataNacional}> | 
					
						
							|  |  |  |               {({ geographies }: { geographies: AmbitoGeography[] }) => geographies.map((geo) => { | 
					
						
							|  |  |  |                 const nombreNormalizado = normalizarTexto(geo.properties.nombre); | 
					
						
							|  |  |  |                 const esCABA = nombreNormalizado === 'CIUDAD AUTONOMA DE BUENOS AIRES'; | 
					
						
							|  |  |  |                 const resultado = resultadosNacionalesPorNombre.get(nombreNormalizado); | 
					
						
							|  |  |  |                 const esProvinciaActiva = provinciaDistritoId && resultado?.ambitoId === provinciaDistritoId; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return ( | 
					
						
							|  |  |  |                   <Geography | 
					
						
							|  |  |  |                     key={geo.rsmKey} | 
					
						
							|  |  |  |                     geography={geo} | 
					
						
							|  |  |  |                     ref={esCABA ? cabaPathRef : undefined} | 
					
						
							|  |  |  |                     className={`rsm-geography ${nivel !== 'pais' ? 'rsm-geography-faded' : ''}`} | 
					
						
							| 
									
										
										
										
											2025-09-22 09:08:43 -03:00
										 |  |  |                     style={{ visibility: esCABA ? 'hidden' : (esProvinciaActiva ? 'hidden' : 'visible') }} | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |                     fill={nivel === 'pais' ? (resultado?.colorGanador || DEFAULT_MAP_COLOR) : FADED_BACKGROUND_COLOR} | 
					
						
							|  |  |  |                     onClick={() => !esCABA && resultado && onAmbitoSelect(resultado.ambitoId, 'provincia', resultado.ambitoNombre)} | 
					
						
							|  |  |  |                     data-tooltip-id="mapa-tooltip" | 
					
						
							|  |  |  |                     data-tooltip-content={geo.properties.nombre} | 
					
						
							|  |  |  |                   /> | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |               })} | 
					
						
							|  |  |  |             </Geographies> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             {provinciaDistritoId && nombreProvinciaActiva && ( | 
					
						
							|  |  |  |               <Suspense fallback={null}> | 
					
						
							|  |  |  |                 <MapaProvincial | 
					
						
							|  |  |  |                   eleccionId={eleccionId} | 
					
						
							|  |  |  |                   categoriaId={categoriaId} | 
					
						
							|  |  |  |                   distritoId={provinciaDistritoId} | 
					
						
							|  |  |  |                   nombreProvincia={nombreProvinciaActiva} | 
					
						
							|  |  |  |                   nombreMunicipioSeleccionado={nombreMunicipioSeleccionado} | 
					
						
							|  |  |  |                   onMunicipioSelect={(ambitoId, nombre) => onAmbitoSelect(ambitoId, 'municipio', nombre)} | 
					
						
							|  |  |  |                   onCalculatedCenter={handleCalculatedCenter} | 
					
						
							|  |  |  |                   nivel={nivel as 'provincia' | 'municipio'} | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |                 /> | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |               </Suspense> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |           </ZoomableGroup> | 
					
						
							|  |  |  |         </ComposableMap> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       {nivel === 'pais' && ( | 
					
						
							|  |  |  |         <div id="caba-lupa-anchor" className="caba-magnifier-container" style={lupaStyle} ref={lupaRef}> | 
					
						
							|  |  |  |           {(() => { | 
					
						
							|  |  |  |             const resultadoCABA = resultadosNacionalesPorNombre.get("CIUDAD AUTONOMA DE BUENOS AIRES"); | 
					
						
							|  |  |  |             const fillColor = resultadoCABA?.colorGanador || DEFAULT_MAP_COLOR; | 
					
						
							|  |  |  |             const handleClick = () => { | 
					
						
							|  |  |  |               if (resultadoCABA) { | 
					
						
							|  |  |  |                 onAmbitoSelect(resultadoCABA.ambitoId, 'provincia', resultadoCABA.ambitoNombre); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             return <CabaLupa fillColor={fillColor} onClick={handleClick} />; | 
					
						
							|  |  |  |           })()} | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       )} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |       <Tooltip id="mapa-tooltip" key={nivel} /> | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |     </div> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; |