203 lines
5.5 KiB
JavaScript
203 lines
5.5 KiB
JavaScript
/**
|
|
* Bootstrap shell-модалки (.wesp-shell-modal) — единая анимация открытия/закрытия.
|
|
*/
|
|
(function (global) {
|
|
const WIRED = "data-wesp-shell-modal-wired";
|
|
const ALLOW_HIDE = "data-wesp-shell-allow-hide";
|
|
const MODAL_MS = 240;
|
|
|
|
function prefersReducedMotion() {
|
|
return global.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false;
|
|
}
|
|
|
|
function getDialog(el) {
|
|
return el?.querySelector(".modal-dialog--shell");
|
|
}
|
|
|
|
function wireShellModal(el) {
|
|
if (!el || el.getAttribute(WIRED)) return;
|
|
el.setAttribute(WIRED, "1");
|
|
|
|
el.addEventListener("show.bs.modal", function () {
|
|
el.classList.remove("wesp-shell-closing");
|
|
if (prefersReducedMotion()) {
|
|
el.classList.add("wesp-shell-visible");
|
|
return;
|
|
}
|
|
el.classList.remove("wesp-shell-visible");
|
|
requestAnimationFrame(function () {
|
|
requestAnimationFrame(function () {
|
|
el.classList.add("wesp-shell-visible");
|
|
});
|
|
});
|
|
});
|
|
|
|
el.addEventListener("shown.bs.modal", function () {
|
|
el.removeAttribute("aria-hidden");
|
|
});
|
|
|
|
el.addEventListener("hide.bs.modal", function (e) {
|
|
if (el.getAttribute(ALLOW_HIDE) === "1") {
|
|
el.removeAttribute(ALLOW_HIDE);
|
|
el.classList.remove("wesp-shell-visible", "wesp-shell-closing");
|
|
return;
|
|
}
|
|
|
|
if (prefersReducedMotion()) {
|
|
el.classList.remove("wesp-shell-visible");
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
el.classList.add("wesp-shell-closing");
|
|
el.classList.remove("wesp-shell-visible");
|
|
|
|
const dialog = getDialog(el);
|
|
let finished = false;
|
|
const finish = function () {
|
|
if (finished) return;
|
|
finished = true;
|
|
el.setAttribute(ALLOW_HIDE, "1");
|
|
const instance = global.bootstrap?.Modal?.getInstance(el);
|
|
instance?.hide();
|
|
};
|
|
|
|
if (dialog) {
|
|
dialog.addEventListener(
|
|
"transitionend",
|
|
function (ev) {
|
|
if (ev.target === dialog) finish();
|
|
},
|
|
{ once: true }
|
|
);
|
|
}
|
|
global.setTimeout(finish, MODAL_MS + 50);
|
|
});
|
|
|
|
el.addEventListener("hidden.bs.modal", function () {
|
|
el.classList.remove("wesp-shell-closing", "wesp-shell-visible");
|
|
el.setAttribute("aria-hidden", "true");
|
|
});
|
|
}
|
|
|
|
function wireAllShellModals() {
|
|
document.querySelectorAll(".modal.wesp-shell-modal").forEach(wireShellModal);
|
|
}
|
|
|
|
function show(modalId) {
|
|
const el = document.getElementById(modalId);
|
|
if (!el) return null;
|
|
wireShellModal(el);
|
|
const instance = global.bootstrap?.Modal?.getOrCreateInstance(el, {
|
|
backdrop: true,
|
|
keyboard: true,
|
|
focus: true,
|
|
});
|
|
instance?.show();
|
|
return instance;
|
|
}
|
|
|
|
function hide(modalId) {
|
|
const el = document.getElementById(modalId);
|
|
if (!el) return;
|
|
const instance = global.bootstrap?.Modal?.getInstance(el);
|
|
if (instance) instance.hide();
|
|
}
|
|
|
|
function whenPageEnterDone(run) {
|
|
const html = document.documentElement;
|
|
let ran = false;
|
|
let observer = null;
|
|
let fallbackTimer = 0;
|
|
|
|
const once = function () {
|
|
if (ran) return;
|
|
ran = true;
|
|
if (observer) {
|
|
observer.disconnect();
|
|
observer = null;
|
|
}
|
|
if (fallbackTimer) {
|
|
global.clearTimeout(fallbackTimer);
|
|
fallbackTimer = 0;
|
|
}
|
|
requestAnimationFrame(run);
|
|
};
|
|
|
|
if (html.classList.contains("wesp-page-enter-done")) {
|
|
once();
|
|
return;
|
|
}
|
|
if (
|
|
!html.classList.contains("wesp-page-enter-pending") &&
|
|
!html.classList.contains("wesp-page-enter-ready")
|
|
) {
|
|
once();
|
|
return;
|
|
}
|
|
|
|
observer = new MutationObserver(function () {
|
|
if (!html.classList.contains("wesp-page-enter-done")) return;
|
|
once();
|
|
});
|
|
observer.observe(html, { attributes: true, attributeFilter: ["class"] });
|
|
fallbackTimer = global.setTimeout(function () {
|
|
if (!html.classList.contains("wesp-page-enter-done")) return;
|
|
once();
|
|
}, 1200);
|
|
}
|
|
|
|
function animateGridCards(gridSelector, options) {
|
|
if (prefersReducedMotion()) return;
|
|
const force = Boolean(options && options.force);
|
|
|
|
const run = function () {
|
|
const grid = document.querySelector(gridSelector);
|
|
if (!grid) return;
|
|
|
|
const cards = grid.querySelectorAll(".dispenser-card, .component-card, .report-card");
|
|
if (
|
|
!force &&
|
|
cards.length > 0 &&
|
|
Array.from(cards).every(function (card) {
|
|
return card.classList.contains("wesp-grid-enter-item");
|
|
})
|
|
) {
|
|
return;
|
|
}
|
|
|
|
cards.forEach(function (card, i) {
|
|
card.classList.remove("wesp-grid-enter-item");
|
|
card.style.removeProperty("--wesp-grid-i");
|
|
void card.offsetWidth;
|
|
card.style.setProperty("--wesp-grid-i", String(Math.min(i, 12)));
|
|
card.classList.add("wesp-grid-enter-item");
|
|
});
|
|
};
|
|
|
|
whenPageEnterDone(run);
|
|
}
|
|
|
|
function revealContent(el) {
|
|
if (!el || prefersReducedMotion()) return;
|
|
el.classList.remove("wesp-content-reveal");
|
|
void el.offsetWidth;
|
|
el.classList.add("wesp-content-reveal");
|
|
}
|
|
|
|
global.WespZootechModals = {
|
|
show,
|
|
hide,
|
|
wireAllShellModals,
|
|
animateGridCards,
|
|
revealContent,
|
|
prefersReducedMotion,
|
|
};
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", wireAllShellModals);
|
|
} else {
|
|
wireAllShellModals();
|
|
}
|
|
})(typeof window !== "undefined" ? window : globalThis);
|