Refinamiento de Funciones y Estética de Mapa
This commit is contained in:
		| @@ -9,6 +9,7 @@ import { API_BASE_URL, assetBaseUrl } from '../../../../apiService'; | ||||
| import type { ResultadoMapaDto, AmbitoGeography } from '../../../../types/types'; | ||||
| import { MapaProvincial } from './MapaProvincial'; | ||||
| import { CabaLupa } from './CabaLupa'; | ||||
| import { BiZoomIn, BiZoomOut } from "react-icons/bi"; | ||||
|  | ||||
| const DEFAULT_MAP_COLOR = '#E0E0E0'; | ||||
| const FADED_BACKGROUND_COLOR = '#F0F0F0'; | ||||
| @@ -17,7 +18,7 @@ const normalizarTexto = (texto: string = '') => texto.trim().toUpperCase().norma | ||||
| type PointTuple = [number, number]; | ||||
|  | ||||
| const PROVINCE_VIEW_CONFIG: Record<string, { center: PointTuple; zoom: number }> = { | ||||
|   "BUENOS AIRES": { center: [-60.5, -37.3], zoom: 5.5 }, | ||||
|   "BUENOS AIRES": { center: [-60.5, -37.3], zoom: 5 }, | ||||
|   "SANTA CRUZ": { center: [-69.5, -48.8], zoom: 5 }, | ||||
|   "CIUDAD AUTONOMA DE BUENOS AIRES": { center: [-58.45, -34.6], zoom: 85 }, | ||||
|   "CHUBUT": { center: [-68.5, -44.5], zoom: 5.5 }, | ||||
| @@ -31,7 +32,6 @@ const LUPA_SIZE_RATIO = 0.2; | ||||
| const MIN_LUPA_SIZE_PX = 100; | ||||
| const MAX_LUPA_SIZE_PX = 180; | ||||
|  | ||||
|  | ||||
| interface MapaNacionalProps { | ||||
|   eleccionId: number; | ||||
|   categoriaId: number; | ||||
| @@ -41,10 +41,19 @@ interface MapaNacionalProps { | ||||
|   provinciaDistritoId: string | null; | ||||
|   onAmbitoSelect: (ambitoId: string, nivel: 'provincia' | 'municipio', nombre: string) => void; | ||||
|   onVolver: () => void; | ||||
|   isMobileView: boolean; | ||||
| } | ||||
|  | ||||
| export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nombreProvinciaActiva, provinciaDistritoId, onAmbitoSelect, onVolver }: MapaNacionalProps) => { | ||||
|   const [position, setPosition] = useState({ zoom: 1, center: [-65, -40] as PointTuple }); | ||||
| // --- CONFIGURACIONES DEL MAPA --- | ||||
| const desktopProjectionConfig = { scale: 700, center: [-65, -40] as [number, number] }; | ||||
| const mobileProjectionConfig = { scale: 1100, center: [-64, -41] as [number, number] }; | ||||
|  | ||||
| export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nombreProvinciaActiva, provinciaDistritoId, onAmbitoSelect, onVolver, isMobileView }: MapaNacionalProps) => { | ||||
|   const [position, setPosition] = useState({ | ||||
|     zoom: isMobileView ? 1.5 : 1.05, // 1.5 para móvil, 1.05 para desktop | ||||
|     center: [-65, -40] as PointTuple | ||||
|   }); | ||||
|   const initialProvincePositionRef = useRef<{ zoom: number, center: PointTuple } | null>(null); | ||||
|  | ||||
|   const containerRef = useRef<HTMLDivElement | null>(null); | ||||
|   const lupaRef = useRef<HTMLDivElement | null>(null); | ||||
| @@ -70,21 +79,33 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (nivel === 'pais') { | ||||
|       setPosition({ zoom: 1, center: [-65, -40] }); | ||||
|       setPosition({ | ||||
|         zoom: isMobileView ? 1.4 : 1.05, // 1.5 para móvil, 1.05 para desktop | ||||
|         center: [-65, -40] | ||||
|       }); | ||||
|       // Reseteamos el ref | ||||
|       initialProvincePositionRef.current = null; | ||||
|     } else if (nivel === 'provincia') { | ||||
|       const nombreNormalizado = normalizarTexto(nombreAmbito); | ||||
|       const manualConfig = PROVINCE_VIEW_CONFIG[nombreNormalizado]; | ||||
|  | ||||
|       let provinceConfig = { zoom: 7, center: [-65, -40] as PointTuple }; | ||||
|  | ||||
|       if (manualConfig) { | ||||
|         setPosition(manualConfig); | ||||
|         provinceConfig = manualConfig; | ||||
|       } 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); | ||||
|           setPosition({ zoom: 7, center: centroid as PointTuple }); | ||||
|           provinceConfig = { zoom: 7, center: centroid as PointTuple }; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       setPosition(provinceConfig); | ||||
|  | ||||
|       // --- Guardar el objeto de posición completo en el ref --- | ||||
|       initialProvincePositionRef.current = provinceConfig; | ||||
|     } | ||||
|   }, [nivel, nombreAmbito, geoDataNacional]); | ||||
|  | ||||
| @@ -104,7 +125,7 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom | ||||
|  | ||||
|         const calculatedSize = containerRect.width * LUPA_SIZE_RATIO; | ||||
|         const newLupaSize = Math.max(MIN_LUPA_SIZE_PX, Math.min(calculatedSize, MAX_LUPA_SIZE_PX)); | ||||
|          | ||||
|  | ||||
|         const horizontalOffset = newLupaSize * 0.5; | ||||
|         const verticalOffset = newLupaSize * 0.2; | ||||
|  | ||||
| @@ -132,7 +153,7 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom | ||||
|     if (containerRef.current) { | ||||
|       resizeObserver.observe(containerRef.current); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     let timerId: NodeJS.Timeout; | ||||
|  | ||||
|     if (initialLoadRef.current && nivel === 'pais') { | ||||
| @@ -159,20 +180,93 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom | ||||
|     }; | ||||
|   }, [position, nivel]); | ||||
|  | ||||
|   // --- HANDLERS PARA EL ZOOM --- | ||||
|   const handleZoomIn = () => { | ||||
|     setPosition(prev => ({ | ||||
|       ...prev, | ||||
|       zoom: Math.min(prev.zoom * 1.8, 100) // Multiplica el zoom actual, con un límite | ||||
|     })); | ||||
|   }; | ||||
|  | ||||
|   // --- Lógica de reseteo en handleZoomOut --- | ||||
|   const handleZoomOut = () => { | ||||
|     setPosition(prev => { | ||||
|       const newZoom = Math.max(prev.zoom / 1.8, 1); | ||||
|       const initialPos = initialProvincePositionRef.current; | ||||
|  | ||||
|       // Si estamos en una provincia Y el nuevo zoom es igual o menor que el inicial... | ||||
|       if (initialPos && newZoom <= initialPos.zoom) { | ||||
|         // ...reseteamos a la posición inicial guardada (zoom Y centro). | ||||
|         return initialPos; | ||||
|       } | ||||
|  | ||||
|       // Si no, solo actualizamos el zoom. | ||||
|       return { ...prev, zoom: newZoom }; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   const handleMoveEnd = (newPosition: { coordinates: PointTuple, zoom: number }) => { | ||||
|     // Solo actualizamos el centro (coordenadas), no el zoom, al arrastrar | ||||
|     setPosition(prev => ({ ...prev, center: newPosition.coordinates })); | ||||
|   }; | ||||
|  | ||||
|   const panEnabled = | ||||
|     //isMobileView && | ||||
|     nivel === 'provincia' && | ||||
|     initialProvincePositionRef.current !== null && | ||||
|     position.zoom > initialProvincePositionRef.current.zoom && | ||||
|     !nombreMunicipioSeleccionado; | ||||
|  | ||||
|   const showZoomControls = nivel === 'provincia'; | ||||
|  | ||||
|   // --- FUNCIÓN DE FILTRO --- | ||||
|   const filterInteractionEvents = (event: any) => { | ||||
|     // La librería pasa un objeto de evento que contiene el evento original del navegador. | ||||
|     // Si el evento original es de la rueda del ratón ('wheel'), siempre lo bloqueamos. | ||||
|     if (event.sourceEvent && event.sourceEvent.type === 'wheel') { | ||||
|       return false; | ||||
|     } | ||||
|     // Para cualquier otro evento (arrastre, etc.), la decisión depende de nuestra lógica `panEnabled`. | ||||
|     return panEnabled; | ||||
|   }; | ||||
|  | ||||
|   // --- LÓGICA PARA DESHABILITAR EL BOTÓN --- | ||||
|   const isZoomOutDisabled = | ||||
|     (nivel === 'provincia' && initialProvincePositionRef.current && position.zoom <= initialProvincePositionRef.current.zoom) || | ||||
|     (nivel === 'pais' && position.zoom <= (isMobileView ? 1.4 : 1.05)); | ||||
|  | ||||
|   return ( | ||||
|     <div className="mapa-componente-container" ref={containerRef}> | ||||
|       {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> | ||||
|       )} | ||||
|  | ||||
|       {nivel !== 'pais' && <button onClick={onVolver} className="mapa-volver-btn">← Volver</button>} | ||||
|  | ||||
|       <div className="mapa-render-area"> | ||||
|         <ComposableMap | ||||
|           projection="geoMercator" | ||||
|           projectionConfig={{ scale: 700, center: [-65, -40] }} | ||||
|           projectionConfig={isMobileView ? mobileProjectionConfig : desktopProjectionConfig} | ||||
|           style={{ width: "100%", height: "100%" }} | ||||
|         > | ||||
|           <ZoomableGroup | ||||
|             center={position.center} | ||||
|             zoom={position.zoom} | ||||
|             filterZoomEvent={() => false} | ||||
|             onMoveEnd={handleMoveEnd} | ||||
|             filterZoomEvent={filterInteractionEvents} | ||||
|           > | ||||
|             <Geographies geography={geoDataNacional}> | ||||
|               {({ geographies }: { geographies: AmbitoGeography[] }) => geographies.map((geo) => { | ||||
| @@ -233,7 +327,7 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom | ||||
|         </div> | ||||
|       )} | ||||
|  | ||||
|       <Tooltip id="mapa-tooltip" /> | ||||
|       <Tooltip id="mapa-tooltip" key={nivel} /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user