130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
/* ==========================================================================
|
|
ECOSISTEMA IDE — Cambio in-place entre tarjetas y paneles de unidad
|
|
========================================================================== */
|
|
(function () {
|
|
'use strict';
|
|
|
|
const section = document.getElementById('ecosistema-ide');
|
|
if (!section) return;
|
|
|
|
const stateEcosystem = section.querySelector('[data-eco-state="ecosystem"]');
|
|
const stateUnit = section.querySelector('[data-eco-state="unit"]');
|
|
const panels = Array.from(section.querySelectorAll('[data-eco-panel]'));
|
|
const triggers = Array.from(section.querySelectorAll('[data-eco-open]'));
|
|
const backBtn = section.querySelector('[data-eco-close]');
|
|
const pills = Array.from(section.querySelectorAll('.eco-pill'));
|
|
|
|
let lastFocusedTrigger = null;
|
|
|
|
/* --- Mostrar panel de una unidad --- */
|
|
function openUnit(id) {
|
|
const panel = panels.find(p => p.dataset.ecoPanel === id);
|
|
if (!panel) return;
|
|
|
|
// Guardamos el trigger que originó el cambio (para devolver foco)
|
|
const origin = triggers.find(t => t.dataset.ecoOpen === id);
|
|
if (origin) lastFocusedTrigger = origin;
|
|
|
|
// Ocultar ecosistema, mostrar estado unidad
|
|
stateEcosystem.hidden = true;
|
|
stateUnit.hidden = false;
|
|
|
|
// Alternar paneles
|
|
panels.forEach(p => {
|
|
const isActive = p.dataset.ecoPanel === id;
|
|
p.hidden = !isActive;
|
|
if (isActive) {
|
|
// Re-trigger de la animación de entrada
|
|
p.style.animation = 'none';
|
|
// Forzar reflow
|
|
void p.offsetWidth;
|
|
p.style.animation = '';
|
|
}
|
|
});
|
|
|
|
// Aria expandido en tarjetas
|
|
triggers.forEach(t => {
|
|
const isThis = t.dataset.ecoOpen === id;
|
|
t.setAttribute('aria-expanded', isThis ? 'true' : 'false');
|
|
});
|
|
|
|
// Pills activas
|
|
pills.forEach(pill => {
|
|
pill.classList.toggle('is-active', pill.dataset.ecoOpen === id);
|
|
});
|
|
|
|
// Foco accesible al título del panel
|
|
const title = panel.querySelector('.eco-panel__title');
|
|
if (title) {
|
|
title.setAttribute('tabindex', '-1');
|
|
// Pequeño timeout para asegurar visibilidad tras el cambio de estado
|
|
requestAnimationFrame(() => title.focus({ preventScroll: false }));
|
|
}
|
|
|
|
// Scroll suave al inicio de la sección (solo si quedó fuera de vista)
|
|
const rect = section.getBoundingClientRect();
|
|
if (rect.top < -80) {
|
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
}
|
|
|
|
/* --- Volver al estado ecosistema --- */
|
|
function closeUnit() {
|
|
stateUnit.hidden = true;
|
|
stateEcosystem.hidden = false;
|
|
|
|
triggers.forEach(t => t.setAttribute('aria-expanded', 'false'));
|
|
pills.forEach(p => p.classList.remove('is-active'));
|
|
|
|
// Devolver foco a la tarjeta origen
|
|
if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') {
|
|
lastFocusedTrigger.focus({ preventScroll: false });
|
|
} else {
|
|
// Fallback: primer botón del grid
|
|
const first = triggers[0];
|
|
if (first) first.focus({ preventScroll: false });
|
|
}
|
|
}
|
|
|
|
/* --- Listeners --- */
|
|
triggers.forEach(trigger => {
|
|
trigger.addEventListener('click', (e) => {
|
|
const id = trigger.dataset.ecoOpen;
|
|
if (!id) return;
|
|
// Si es un <a> real (academy), dejamos que navegue
|
|
if (trigger.tagName === 'A' && trigger.getAttribute('href')) return;
|
|
e.preventDefault();
|
|
openUnit(id);
|
|
});
|
|
|
|
// Soporte teclado para tarjetas no-button (por si acaso)
|
|
trigger.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
const id = trigger.dataset.ecoOpen;
|
|
if (!id) return;
|
|
if (trigger.tagName === 'A') return;
|
|
e.preventDefault();
|
|
openUnit(id);
|
|
}
|
|
});
|
|
});
|
|
|
|
if (backBtn) backBtn.addEventListener('click', closeUnit);
|
|
|
|
// Escape cierra el panel expandido
|
|
section.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && !stateUnit.hidden) {
|
|
closeUnit();
|
|
}
|
|
});
|
|
|
|
/* --- Deep link opcional por hash (#eco-projects) sin recargar --- */
|
|
function readHash() {
|
|
const hash = window.location.hash.replace('#eco-', '');
|
|
if (['excellence', 'projects', 'digital'].includes(hash)) {
|
|
openUnit(hash);
|
|
}
|
|
}
|
|
window.addEventListener('hashchange', readHash);
|
|
readHash();
|
|
})(); |