PageSourceSearch

https://cocunat.com/js/megamenu/megamenu-collapse.js

js cocunat.com collected 2026-09-24 17:34:42 UTC 4,463 bytes, 99 lines download raw bytes

1// public/js/megamenu/megamenu-collapse.js
2//
3// Encaje responsivo del nav del megamenú en desktop. El breakpoint lg solo
4// no basta: el número y la longitud de los items dependen del documento
5// publicado y del idioma (labels alemanes, menús con muchos items…), así que
6// MEDIMOS y aplicamos una CASCADA DE DENSIDAD antes de rendirnos al burger:
7//
8//   1. padding completo (px-7)            → cabe: nav normal
9//   2. padding compacto (body--nav-compact: px-2.5 + gap-2 en la cápsula)
10//                                          → cabe: nav compacto
11//   3. body--nav-collapsed                 → burger + drawer también en desktop
12//
13// La densidad va por CLASE y no por breakpoint a propósito: con el antiguo
14// lg:max-xl:px-2.5 el encaje no era monotónico al redimensionar (burger a
15// ~1400px y reaparición del nav al cruzar 1280px, cuando el media query
16// encogía el padding de golpe). Con la cascada, al encoger la ventana la
17// secuencia es siempre nav → nav compacto → burger, sin saltos atrás.
18//
19// En modo colapsado el nav queda `invisible` (visibility, NO display:
20// conserva su layout, lo que permite re-medir y decidir cuándo volver a
21// expandir). Los toggles de prueba y la medición ocurren dentro del mismo
22// frame (rAF / parser-blocking), antes del paint: no hay parpadeo.
23//
24// Como el resto de flags (body--past-hero, body--drawer-open…), aquí solo se
25// mide y se togglean clases: el pintado es de las variantes Tailwind del
26// header. El cambio burger↔nav se anuncia con eventos `megamenu:collapse` /
27// `megamenu:expand` para que megamenu-panel.js y megamenu-drawer.js cierren
28// lo que tengan abierto.
29//
30// Histéresis natural: colapsado aparece el hamburger (el nav pierde ese
31// ancho), así que solo re-expandimos cuando el contenido cabe INCLUSO con el
32// hamburger presente — no hay parpadeo en el límite.
33(function () {
34  var nav = document.querySelector('[data-megamenu-nav]');
35  if (!nav) return;
36  var body = document.body;
37
38  var EPSILON = 1; // margen frente a redondeos subpíxel
39
40  // Ancho que necesita el contenido: suma de los nav-links (whitespace-nowrap
41  // + min-width:auto → nunca encogen por debajo de su contenido; el nav usa
42  // gap-0, no hay separaciones que sumar). getBoundingClientRect funciona
43  // igual con visibility:hidden, que conserva layout.
44  function contentWidth() {
45    var total = 0;
46    for (var i = 0; i < nav.children.length; i++) {
47      total += nav.children[i].getBoundingClientRect().width;
48    }
49    return total;
50  }
51
52  // Leer layout tras togglear una clase fuerza el reflow síncrono: cada paso
53  // de la cascada mide la densidad que acaba de aplicar.
54  function overflows() {
55    var navWidth = nav.getBoundingClientRect().width;
56    // <lg el nav es display:none (todo mide 0): mandan las clases max-lg del
57    // header, aquí "cabe" y se limpian las clases.
58    return navWidth > 0 && contentWidth() - navWidth > EPSILON;
59  }
60
61  function apply(compact, collapsed) {
62    var wasCollapsed = body.classList.contains('body--nav-collapsed');
63    body.classList.toggle('body--nav-compact', compact);
64    body.classList.toggle('body--nav-collapsed', collapsed);
65    if (wasCollapsed !== collapsed) {
66      window.dispatchEvent(new CustomEvent(collapsed ? 'megamenu:collapse' : 'megamenu:expand'));
67    }
68  }
69
70  function measure() {
71    // La cascada se re-evalúa desde cero en cada medición (independiente del
72    // estado anterior): es idempotente, así que el ResizeObserver que
73    // disparan los propios toggles converge en una pasada extra sin bucles.
74    body.classList.remove('body--nav-compact');
75    if (!overflows()) return apply(false, false);
76    body.classList.add('body--nav-compact');
77    if (!overflows()) return apply(true, false);
78    apply(true, true);
79  }
80
81  var raf = null;
82  function schedule() {
83    if (raf) return;
84    raf = requestAnimationFrame(function () {
85      raf = null;
86      measure();
87    });
88  }
89
90  if (window.ResizeObserver) new ResizeObserver(schedule).observe(nav);
91  window.addEventListener('resize', schedule, { passive: true });
92  // Las fuentes custom (brownRegular) cambian el ancho de los labels al cargar.
93  if (document.fonts && document.fonts.ready && document.fonts.ready.then) {
94    document.fonts.ready.then(schedule);
95  }
96  // Primera medición síncrona (el script es parser-blocking justo tras el
97  // header): el nav nunca llega a pintarse por encima de los iconos.
98  measure();
99})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.