Initial commit: site monorepo with API, web, and infra.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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);
|
||||
Reference in New Issue
Block a user