129 lines
3.9 KiB
JavaScript
129 lines
3.9 KiB
JavaScript
import { prefersReducedMotion } from "../shared/media.js";
|
|
|
|
export function initExperienceExpand(masonry) {
|
|
if (!masonry) return;
|
|
const btn = document.querySelector("[data-exp-toggle]");
|
|
const extras = masonry.extraItems;
|
|
if (!btn || extras.length === 0) return;
|
|
|
|
const labelExpand = "Показать все";
|
|
const labelCollapse = "Свернуть";
|
|
const reducedMotion = prefersReducedMotion();
|
|
let locked = false;
|
|
|
|
const wait = (ms) => new Promise((resolve) => window.setTimeout(resolve, ms));
|
|
|
|
function animateOpen(el, delayMs) {
|
|
const card = el.querySelector(".exp-card");
|
|
if (!card) return Promise.resolve();
|
|
if (reducedMotion) {
|
|
el.setAttribute("aria-hidden", "false");
|
|
card.style.opacity = "1";
|
|
card.style.transform = "translateY(0)";
|
|
card.style.pointerEvents = "";
|
|
return Promise.resolve();
|
|
}
|
|
const duration = 280;
|
|
el.setAttribute("aria-hidden", "false");
|
|
card.style.opacity = "0";
|
|
card.style.transform = "translateY(12px)";
|
|
card.style.pointerEvents = "";
|
|
card.style.willChange = "opacity, transform";
|
|
|
|
return new Promise((resolve) => {
|
|
window.setTimeout(() => {
|
|
card.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
|
|
card.style.opacity = "1";
|
|
card.style.transform = "translateY(0)";
|
|
}, delayMs);
|
|
|
|
window.setTimeout(() => {
|
|
card.style.transition = "";
|
|
card.style.opacity = "";
|
|
card.style.transform = "";
|
|
card.style.willChange = "";
|
|
resolve();
|
|
}, delayMs + duration + 30);
|
|
});
|
|
}
|
|
|
|
function animateClose(el, delayMs) {
|
|
const card = el.querySelector(".exp-card");
|
|
if (!card) return Promise.resolve();
|
|
if (reducedMotion) {
|
|
el.setAttribute("aria-hidden", "true");
|
|
card.style.opacity = "0";
|
|
card.style.transform = "translateY(12px)";
|
|
card.style.pointerEvents = "none";
|
|
return Promise.resolve();
|
|
}
|
|
const duration = 240;
|
|
card.style.opacity = "1";
|
|
card.style.transform = "translateY(0)";
|
|
card.style.willChange = "opacity, transform";
|
|
|
|
return new Promise((resolve) => {
|
|
window.setTimeout(() => {
|
|
card.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
|
|
card.style.opacity = "0";
|
|
card.style.transform = "translateY(12px)";
|
|
}, delayMs);
|
|
|
|
window.setTimeout(() => {
|
|
card.style.transition = "";
|
|
card.style.opacity = "";
|
|
card.style.transform = "";
|
|
card.style.willChange = "";
|
|
el.setAttribute("aria-hidden", "true");
|
|
card.style.opacity = "0";
|
|
card.style.transform = "translateY(12px)";
|
|
card.style.pointerEvents = "none";
|
|
resolve();
|
|
}, delayMs + duration + 30);
|
|
});
|
|
}
|
|
|
|
function setExpanded(expanded) {
|
|
btn.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
btn.textContent = expanded ? labelCollapse : labelExpand;
|
|
}
|
|
|
|
btn.addEventListener("click", async () => {
|
|
if (locked) return;
|
|
locked = true;
|
|
btn.disabled = true;
|
|
|
|
const next = btn.getAttribute("aria-expanded") !== "true";
|
|
setExpanded(next);
|
|
|
|
if (next) {
|
|
masonry.setExpanded(true);
|
|
const jobs = [];
|
|
extras.forEach((el, i) => jobs.push(animateOpen(el, i * 45)));
|
|
await Promise.all(jobs);
|
|
} else {
|
|
const reversed = Array.from(extras).reverse();
|
|
const jobs = [];
|
|
reversed.forEach((el, i) => jobs.push(animateClose(el, i * 35)));
|
|
await Promise.all(jobs);
|
|
masonry.setExpanded(false);
|
|
await wait(10);
|
|
}
|
|
|
|
masonry.relayout();
|
|
btn.disabled = false;
|
|
locked = false;
|
|
});
|
|
|
|
extras.forEach((el) => {
|
|
const card = el.querySelector(".exp-card");
|
|
el.setAttribute("aria-hidden", "true");
|
|
if (!card) return;
|
|
card.style.opacity = "0";
|
|
card.style.transform = "translateY(12px)";
|
|
card.style.pointerEvents = "none";
|
|
});
|
|
masonry.setExpanded(false);
|
|
masonry.relayout();
|
|
}
|