export function initExperienceMasonry() { const list = document.querySelector("#experience-list"); if (!list) return null; const allItems = Array.from(list.querySelectorAll(".experience-grid__item")); const extraItems = allItems.filter((el) => el.hasAttribute("data-exp-extra")); const baseItems = allItems.filter((el) => !el.hasAttribute("data-exp-extra")); const gap = 16; let rafId = 0; let expanded = false; let fullHeight = 0; let collapsedHeight = 0; extraItems.forEach((el) => el.removeAttribute("hidden")); function columnCountForWidth(w) { if (w >= 1120) return 3; if (w >= 760) return 2; return 1; } function performLayout() { if (!allItems.length) { list.style.height = "0px"; return; } const listWidth = list.clientWidth; // Колонки считаем по фактической ширине списка, чтобы большие боковые // отступы секции не ломали раскладку и связанные hover-эффекты карточек. const columns = columnCountForWidth(listWidth); const colWidth = (listWidth - gap * (columns - 1)) / columns; const heights = Array(columns).fill(0); const positions = new Map(); allItems.forEach((item) => { item.style.width = `${colWidth}px`; const minHeight = Math.min(...heights); const colIndex = heights.indexOf(minHeight); const x = colIndex * (colWidth + gap); const y = heights[colIndex]; item.style.transform = `translate(${x}px, ${y}px)`; heights[colIndex] = y + item.offsetHeight + gap; positions.set(item, { y, h: item.offsetHeight }); }); fullHeight = Math.max(Math.max(...heights) - gap, 0); collapsedHeight = 0; baseItems.forEach((item) => { const p = positions.get(item); if (!p) return; collapsedHeight = Math.max(collapsedHeight, p.y + p.h); }); list.style.height = `${expanded ? fullHeight : collapsedHeight}px`; } function relayout() { if (rafId) window.cancelAnimationFrame(rafId); rafId = window.requestAnimationFrame(performLayout); } function relayoutAfterThemeChange() { relayout(); if (document.fonts?.ready) { document.fonts.ready.then(relayout); } window.requestAnimationFrame(() => { relayout(); window.requestAnimationFrame(relayout); }); } window.addEventListener("site-theme-applied", relayoutAfterThemeChange, { passive: true }); window.addEventListener("resize", relayout, { passive: true }); window.addEventListener("load", relayout, { once: true }); relayout(); function setExpanded(next) { expanded = next; list.style.height = `${expanded ? fullHeight : collapsedHeight}px`; } return { list, relayout, setExpanded, extraItems }; }