/* ========================================================================== ECOSISTEMA IDE — Cambio in-place + deep link por hash URLs válidas: #eco-excellence | #eco-projects | #eco-digital | #ecosistema ========================================================================== */ (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')); const VALID_UNITS = ['excellence', 'projects', 'digital']; const HASH_PREFIX = '#eco-'; const HASH_ECOSYSTEM = '#ecosistema'; let lastFocusedTrigger = null; let currentUnit = null; // null = ecosistema /* ---------- Utilidades de URL ---------- */ function unitFromHash() { const h = window.location.hash; if (h === HASH_ECOSYSTEM) return null; if (h.startsWith(HASH_PREFIX)) { const id = h.slice(HASH_PREFIX.length); return VALID_UNITS.includes(id) ? id : null; } return null; } function hashForUnit(id) { return id ? HASH_PREFIX + id : HASH_ECOSYSTEM; } /* ---------- Render (no toca la URL) ---------- */ function renderUnit(id) { const panel = panels.find(p => p.dataset.ecoPanel === id); if (!panel) return; const origin = triggers.find(t => t.dataset.ecoOpen === id); if (origin) lastFocusedTrigger = origin; stateEcosystem.hidden = true; stateUnit.hidden = false; panels.forEach(p => { const isActive = p.dataset.ecoPanel === id; p.hidden = !isActive; if (isActive) { p.style.animation = 'none'; void p.offsetWidth; // reflow para reiniciar animación p.style.animation = ''; } }); triggers.forEach(t => { t.setAttribute('aria-expanded', t.dataset.ecoOpen === id ? 'true' : 'false'); }); pills.forEach(pill => { pill.classList.toggle('is-active', pill.dataset.ecoOpen === id); }); const title = panel.querySelector('.eco-panel__title'); if (title) { title.setAttribute('tabindex', '-1'); requestAnimationFrame(() => title.focus({ preventScroll: false })); } currentUnit = id; } function renderEcosystem({ restoreFocus = true } = {}) { stateUnit.hidden = true; stateEcosystem.hidden = false; triggers.forEach(t => t.setAttribute('aria-expanded', 'false')); pills.forEach(p => p.classList.remove('is-active')); if (restoreFocus) { if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { lastFocusedTrigger.focus({ preventScroll: false }); } else if (triggers[0]) { triggers[0].focus({ preventScroll: false }); } } currentUnit = null; } /* ---------- API con URL ---------- */ function openUnit(id, { push = true, scroll = true } = {}) { if (!VALID_UNITS.includes(id)) return; renderUnit(id); if (push) { const url = window.location.pathname + window.location.search + hashForUnit(id); history.pushState({ eco: id }, '', url); } if (scroll) { const rect = section.getBoundingClientRect(); if (rect.top < -80 || rect.top > 200) { section.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } } function closeUnit({ push = true, scroll = false } = {}) { renderEcosystem(); if (push) { const url = window.location.pathname + window.location.search + HASH_ECOSYSTEM; history.pushState({ eco: null }, '', url); } if (scroll) { section.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } /* ---------- Listeners de UI ---------- */ triggers.forEach(trigger => { trigger.addEventListener('click', (e) => { const id = trigger.dataset.ecoOpen; if (!id) return; // Academy es : dejamos navegar if (trigger.tagName === 'A' && trigger.getAttribute('href')) return; e.preventDefault(); openUnit(id); }); trigger.addEventListener('keydown', (e) => { if (e.key !== 'Enter' && e.key !== ' ') return; const id = trigger.dataset.ecoOpen; if (!id) return; if (trigger.tagName === 'A') return; e.preventDefault(); openUnit(id); }); }); if (backBtn) backBtn.addEventListener('click', () => closeUnit({ scroll: true })); section.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !stateUnit.hidden) closeUnit(); }); /* ---------- Sincronización con la URL ---------- */ function syncFromLocation() { const id = unitFromHash(); if (id && id !== currentUnit) { renderUnit(id); } else if (!id && currentUnit !== null) { renderEcosystem({ restoreFocus: false }); } } // Carga inicial: si el hash trae unidad, abrir + scroll (function initFromUrl() { const id = unitFromHash(); if (id) { renderUnit(id); requestAnimationFrame(() => { section.scrollIntoView({ behavior: 'auto', block: 'start' }); }); } else { renderEcosystem({ restoreFocus: false }); } })(); // Cambios manuales del hash (pegar link, editar barra, links compartidos) window.addEventListener('hashchange', syncFromLocation); // Atrás/adelante del navegador window.addEventListener('popstate', syncFromLocation); /* ---------- API pública opcional ---------- */ window.EcoIDE = { open: openUnit, close: closeUnit, current: () => currentUnit, }; })();