Fix Arrastre Mapa en Zoom

This commit is contained in:
2025-09-22 09:08:43 -03:00
parent 3750d1a56d
commit 5a8bee52d5
3 changed files with 29 additions and 66 deletions

View File

@@ -296,7 +296,7 @@
}
/* --- MAPA Y ELEMENTOS ASOCIADOS (sin cambios) --- */
/* --- MAPA Y ELEMENTOS ASOCIADOS --- */
.mapa-componente-container {
width: 100%;
height: 100%;
@@ -329,6 +329,11 @@
transition: transform 0.75s ease-in-out;
}
/* AÑADIDO: Desactivar la transición durante el arrastre */
.rsm-zoomable-group.panning {
transition: none;
}
.panel-main-content.panel-collapsed .mapa-column {
flex: 1 1 100%;
}
@@ -368,7 +373,6 @@
}
.rsm-geography {
cursor: pointer;
stroke: #000000;
stroke-width: 0.25px;
outline: none;
@@ -579,18 +583,14 @@
/* --- ESTILOS DE CURSOR PARA EL ARRASTRE DEL MAPA --- */
.map-locked .rsm-geography {
cursor: pointer;
/* Cursor normal de clic */
}
.map-pannable .rsm-geography {
cursor: grab;
/* Indica que el mapa se puede arrastrar */
}
.map-pannable .rsm-geography:active {
cursor: grabbing;
/* Indica que se está arrastrando */
}
/* El cursor 'grabbing' se aplica automáticamente por el navegador durante el arrastre */
/* --- MEDIA QUERY PARA RESPONSIVE (ENFOQUE FINAL CON CAPAS) --- */
@media (max-width: 800px) {

View File

@@ -1,3 +1,4 @@
// src/features/legislativas/nacionales/components/MapaNacional.tsx
import axios from 'axios';
import { Suspense, useState, useEffect, useCallback, useRef } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query';
@@ -53,13 +54,14 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
zoom: isMobileView ? 1.5 : 1.05, // 1.5 para móvil, 1.05 para desktop
center: [-65, -40] as PointTuple
});
const [isPanning, setIsPanning] = useState(false);
const initialProvincePositionRef = useRef<{ zoom: number, center: PointTuple } | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const lupaRef = useRef<HTMLDivElement | null>(null);
const cabaPathRef = useRef<SVGPathElement | null>(null);
const isAnimatingRef = useRef(false);
const initialLoadRef = useRef(true); // Ref para controlar la carga inicial
const initialLoadRef = useRef(true);
const [lupaStyle, setLupaStyle] = useState<React.CSSProperties>({ opacity: 0 });
@@ -80,10 +82,9 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
useEffect(() => {
if (nivel === 'pais') {
setPosition({
zoom: isMobileView ? 1.4 : 1.05, // 1.5 para móvil, 1.05 para desktop
zoom: isMobileView ? 1.4 : 1.05,
center: [-65, -40]
});
// Reseteamos el ref
initialProvincePositionRef.current = null;
} else if (nivel === 'provincia') {
const nombreNormalizado = normalizarTexto(nombreAmbito);
@@ -103,11 +104,9 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
}
setPosition(provinceConfig);
// --- Guardar el objeto de posición completo en el ref ---
initialProvincePositionRef.current = provinceConfig;
}
}, [nivel, nombreAmbito, geoDataNacional]);
}, [nivel, nombreAmbito, geoDataNacional, isMobileView]);
const resultadosNacionalesPorNombre = new Map<string, ResultadoMapaDto>(mapaDataNacional.map(d => [normalizarTexto(d.ambitoNombre), d]));
const nombreMunicipioSeleccionado = nivel === 'municipio' ? nombreAmbito : null;
@@ -142,29 +141,18 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
};
isAnimatingRef.current = true;
const handleResize = () => {
if (!isAnimatingRef.current) {
updateLupaPosition();
}
};
const handleResize = () => { if (!isAnimatingRef.current) updateLupaPosition(); };
const resizeObserver = new ResizeObserver(handleResize);
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
if (containerRef.current) resizeObserver.observe(containerRef.current);
let timerId: NodeJS.Timeout;
if (initialLoadRef.current && nivel === 'pais') {
// Carga inicial: posicionar inmediatamente
timerId = setTimeout(() => {
updateLupaPosition();
isAnimatingRef.current = false;
}, 0);
initialLoadRef.current = false; // Marcar como ya cargado
initialLoadRef.current = false;
} else {
// Transición de vuelta: esperar a que termine la animación
timerId = setTimeout(() => {
updateLupaPosition();
isAnimatingRef.current = false;
@@ -172,77 +160,53 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
}
return () => {
if (containerRef.current) {
resizeObserver.unobserve(containerRef.current);
}
if (containerRef.current) resizeObserver.unobserve(containerRef.current);
clearTimeout(timerId);
isAnimatingRef.current = false;
};
}, [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
}));
};
const handleZoomIn = () => setPosition(prev => ({ ...prev, zoom: Math.min(prev.zoom * 1.8, 100) }));
// --- 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.
if (initialPos && newZoom <= initialPos.zoom) return initialPos;
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 }));
setIsPanning(false);
};
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`.
if (event.sourceEvent && event.sourceEvent.type === 'wheel') return false;
return panEnabled;
};
// --- LÓGICA PARA DESHABILITAR EL BOTÓN ---
const showZoomControls = nivel === 'provincia';
const isZoomOutDisabled =
(nivel === 'provincia' && initialProvincePositionRef.current && position.zoom <= initialProvincePositionRef.current.zoom) ||
(nivel === 'pais' && position.zoom <= (isMobileView ? 1.4 : 1.05));
const mapContainerClasses = panEnabled ? 'mapa-componente-container map-pannable' : 'mapa-componente-container map-locked';
return (
<div className="mapa-componente-container" ref={containerRef}>
<div className={mapContainerClasses} 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' : ''}`}
@@ -265,8 +229,10 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
<ZoomableGroup
center={position.center}
zoom={position.zoom}
onMoveStart={() => setIsPanning(true)}
onMoveEnd={handleMoveEnd}
filterZoomEvent={filterInteractionEvents}
className={isPanning ? 'panning' : ''}
>
<Geographies geography={geoDataNacional}>
{({ geographies }: { geographies: AmbitoGeography[] }) => geographies.map((geo) => {
@@ -281,9 +247,7 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
geography={geo}
ref={esCABA ? cabaPathRef : undefined}
className={`rsm-geography ${nivel !== 'pais' ? 'rsm-geography-faded' : ''}`}
style={{
visibility: esCABA ? 'hidden' : (esProvinciaActiva ? 'hidden' : 'visible'),
}}
style={{ visibility: esCABA ? 'hidden' : (esProvinciaActiva ? 'hidden' : 'visible') }}
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"
@@ -321,7 +285,6 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
onAmbitoSelect(resultadoCABA.ambitoId, 'provincia', resultadoCABA.ambitoNombre);
}
};
return <CabaLupa fillColor={fillColor} onClick={handleClick} />;
})()}
</div>