refactor: Migra todos los widgets nacionales a CSS Modules para encapsular estilos
Esta refactorización modifica la forma en que los widgets manejan sus estilos para prevenir conflictos con los CSS de los sitios anfitriones donde se incrustan. Se ha migrado el sistema de estilos de CSS global a CSS Modules para todos los componentes principales y sus hijos, asegurando que todas las clases sean únicas y estén aisladas. Cambios principales: - Se actualizan los componentes .tsx para importar y usar los módulos de estilos (`import styles from ...`). - Se renombran los archivos `.css` a `.module.css`. - Se añade una regla en cada módulo para proteger la `font-family` y el `box-sizing` del widget, evitando que sean sobreescritos por estilos externos. - Se ajustan los selectores para librerías de terceros (react-select, react-simple-maps) usando `:global()` para mantener la compatibilidad. - Se mueven las variables CSS de `:root` a las clases principales de cada widget para evitar colisiones en el scope global. Como resultado, los widgets (`HomeCarouselWidget`, `PanelNacionalWidget`, `ResultadosNacionalesCardsWidget`, `CongresoNacionalWidget`) son ahora más robustos, portátiles y visualmente consistentes en cualquier entorno.
This commit is contained in:
@@ -10,7 +10,7 @@ import { Navigation, A11y } from 'swiper/modules';
|
||||
import 'swiper/css';
|
||||
// @ts-ignore
|
||||
import 'swiper/css/navigation';
|
||||
import './HomeCarouselWidget.css';
|
||||
import styles from './HomeCarouselWidget.module.css';
|
||||
|
||||
interface Props {
|
||||
eleccionId: number;
|
||||
@@ -22,14 +22,12 @@ interface Props {
|
||||
const formatPercent = (num: number | null | undefined) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||
const formatNumber = (num: number) => num.toLocaleString('es-AR');
|
||||
|
||||
// --- Lógica de formateo de fecha ---
|
||||
const formatDateTime = (dateString: string | undefined | null) => {
|
||||
if (!dateString) return '...';
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
// Verificar si la fecha es válida
|
||||
if (isNaN(date.getTime())) {
|
||||
return dateString; // Si no se puede parsear, devolver el string original
|
||||
return dateString;
|
||||
}
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
@@ -38,7 +36,7 @@ const formatDateTime = (dateString: string | undefined | null) => {
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${day}/${month}/${year}, ${hours}:${minutes} hs.`;
|
||||
} catch (e) {
|
||||
return dateString; // En caso de cualquier error, devolver el string original
|
||||
return dateString;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,82 +50,94 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
|
||||
if (error || !data) return <div>No se pudieron cargar los datos.</div>;
|
||||
|
||||
return (
|
||||
<div className="home-carousel-widget">
|
||||
<h2 className="widget-title">{titulo}</h2>
|
||||
<div className={styles.homeCarouselWidget}>
|
||||
<h2 className={styles.widgetTitle}>{titulo}</h2>
|
||||
|
||||
<div className="top-stats-bar">
|
||||
<div className={styles.topStatsBar}>
|
||||
<div>
|
||||
<span>Participación</span>
|
||||
<strong>{formatPercent(data.estadoRecuento?.participacionPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className="long-text">Mesas escrutadas</span>
|
||||
<span className="short-text">Escrutado</span>
|
||||
<span className={styles.longText}>Mesas escrutadas</span>
|
||||
<span className={styles.shortText}>Escrutado</span>
|
||||
<strong>{formatPercent(data.estadoRecuento?.mesasTotalizadasPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className="long-text">Votos en blanco</span>
|
||||
<span className="short-text">En blanco</span>
|
||||
<span className={styles.longText}>Votos en blanco</span>
|
||||
<span className={styles.shortText}>En blanco</span>
|
||||
<strong>{formatPercent(data.votosEnBlancoPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className="long-text">Votos totales</span>
|
||||
<span className="short-text">Votos</span>
|
||||
<span className={styles.longText}>Votos totales</span>
|
||||
<span className={styles.shortText}>Votos</span>
|
||||
<strong>{formatNumber(data.votosTotales)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Swiper
|
||||
modules={[Navigation, A11y]}
|
||||
spaceBetween={16}
|
||||
slidesPerView={1.15}
|
||||
navigation
|
||||
breakpoints={{ 640: { slidesPerView: 2 }, 1024: { slidesPerView: 3 }, 1200: { slidesPerView: 3.5 } }} // Añadir breakpoint
|
||||
>
|
||||
{data.resultados.map(candidato => (
|
||||
<SwiperSlide key={candidato.agrupacionId}>
|
||||
<div className="candidate-card" style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}>
|
||||
<div className={styles.carouselContainer}>
|
||||
<Swiper
|
||||
modules={[Navigation, A11y]}
|
||||
spaceBetween={16}
|
||||
slidesPerView={1.3}
|
||||
navigation={{
|
||||
prevEl: `.${styles.navButtonPrev}`,
|
||||
nextEl: `.${styles.navButtonNext}`,
|
||||
}}
|
||||
breakpoints={{
|
||||
320: { slidesPerView: 1.25, spaceBetween: 10 },
|
||||
430: { slidesPerView: 1.4, spaceBetween: 12 },
|
||||
640: { slidesPerView: 2.5 },
|
||||
1024: { slidesPerView: 3 },
|
||||
1200: { slidesPerView: 3.5 }
|
||||
}}
|
||||
>
|
||||
{data.resultados.map(candidato => (
|
||||
<SwiperSlide key={candidato.agrupacionId}>
|
||||
<div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}>
|
||||
|
||||
<div className="candidate-photo-wrapper">
|
||||
<ImageWithFallback
|
||||
src={candidato.fotoUrl ?? undefined}
|
||||
fallbackSrc={`${assetBaseUrl}/default-avatar.png`}
|
||||
alt={candidato.nombreCandidato ?? ''}
|
||||
className="candidate-photo"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.candidatePhotoWrapper}>
|
||||
<ImageWithFallback
|
||||
src={candidato.fotoUrl ?? undefined}
|
||||
fallbackSrc={`${assetBaseUrl}/default-avatar.png`}
|
||||
alt={candidato.nombreCandidato ?? ''}
|
||||
className={styles.candidatePhoto}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="candidate-details">
|
||||
<div className="candidate-info">
|
||||
{candidato.nombreCandidato ? (
|
||||
// CASO 1: Hay un candidato (se muestran dos líneas)
|
||||
<>
|
||||
<span className="candidate-name">
|
||||
{candidato.nombreCandidato}
|
||||
</span>
|
||||
<span className="party-name">
|
||||
<div className={styles.candidateDetails}>
|
||||
<div className={styles.candidateInfo}>
|
||||
{candidato.nombreCandidato ? (
|
||||
<>
|
||||
<span className={styles.candidateName}>
|
||||
{candidato.nombreCandidato}
|
||||
</span>
|
||||
<span className={styles.partyName}>
|
||||
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.candidateName}>
|
||||
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
// CASO 2: No hay candidato (se muestra solo una línea)
|
||||
<span className="candidate-name">
|
||||
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="candidate-results">
|
||||
<span className="percentage">{formatPercent(candidato.porcentaje)}</span>
|
||||
<span className="votes">{formatNumber(candidato.votos)} votos</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.candidateResults}>
|
||||
<span className={styles.percentage}>{formatPercent(candidato.porcentaje)}</span>
|
||||
<span className={styles.votes}>{formatNumber(candidato.votos)} votos</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
<div className={`${styles.navButton} ${styles.navButtonPrev}`}></div>
|
||||
<div className={`${styles.navButton} ${styles.navButtonNext}`}></div>
|
||||
</div>
|
||||
|
||||
<div className="widget-footer">
|
||||
<div className={styles.widgetFooter}>
|
||||
Última actualización: {formatDateTime(data.ultimaActualizacion)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user