/** * Team Grid block — bio expand/collapse animation. * * Native
/ handles the toggle, but CSS transitions on * max-height need a concrete target value to animate to. This module * measures each bio's natural height on open and animates max-height * from current → measured (open) or current → 0 (close) so the * transition fires in both directions. * * The module is enqueued per-block by the team-grid PHP template. It * auto-initializes on import: queries the DOM for any existing * .team-grid__details elements and attaches a toggle handler to each. */ const DETAILS_SELECTOR = '.team-grid__details'; const BIO_WRAP_SELECTOR = '.team-grid__bio-wrap'; const setMaxHeight = (el, value) => { el.style.maxHeight = typeof value === 'number' ? `${value}px` : value; }; const measureBioHeight = (bioWrap) => { // Temporarily allow the inner content to render at its natural size so // scrollHeight reports the real height rather than the clipped height. const previousMax = bioWrap.style.maxHeight; const previousOverflow = bioWrap.style.overflow; setMaxHeight(bioWrap, 'none'); bioWrap.style.overflow = 'visible'; const height = bioWrap.scrollHeight; bioWrap.style.overflow = previousOverflow; setMaxHeight(bioWrap, previousMax || ''); return height; }; const animateOpen = (bioWrap) => { const target = measureBioHeight(bioWrap); setMaxHeight(bioWrap, '0px'); // Force a reflow so the browser registers the starting value before // we change it to the target — otherwise the transition is skipped. // eslint-disable-next-line no-unused-expressions bioWrap.offsetHeight; requestAnimationFrame(() => { setMaxHeight(bioWrap, `${target}px`); }); // After the transition completes, leave max-height at the measured // pixel value. Resetting to 'none' breaks the close animation: the // browser treats 'none' as a discrete keyword value and won't // transition from it to a length. The pixel value gives the close // animation a clean from-state to interpolate from. }; const animateClose = (bioWrap) => { // Capture the current rendered height so the browser has a concrete // starting value for the transition. Without this, going from // 'none' to '0px' snaps instead of animating. const current = bioWrap.getBoundingClientRect().height; setMaxHeight(bioWrap, `${current}px`); // Force a reflow so the browser registers the starting value. // eslint-disable-next-line no-unused-expressions bioWrap.offsetHeight; requestAnimationFrame(() => { setMaxHeight(bioWrap, '0px'); }); }; const attach = (details) => { if (details.dataset.teamGridBound === 'true') { return; } details.dataset.teamGridBound = 'true'; const bioWrap = details.querySelector(BIO_WRAP_SELECTOR); if (!bioWrap) { return; } details.addEventListener('toggle', () => { if (details.open) { animateOpen(bioWrap); } else { animateClose(bioWrap); } }); }; const init = () => { document.querySelectorAll(DETAILS_SELECTOR).forEach(attach); }; init();