const KIOSK_SKELETON_MIN_MS = 300;
const KIOSK_SKELETON_EXIT_MS = 160;
const GRID_CARD_INNER = `
`;
const GROUP_ITEM_INNER = `
`;
export function buildGridSkeletonCards(rowCount = 4) {
return Array.from({ length: rowCount }, () =>
`${GRID_CARD_INNER}
`
).join("");
}
export function buildDispenserGridSkeletonHtml(rowCount = 4) {
return buildGridSkeletonCards(rowCount);
}
export function buildPeriodRecipeGridSkeletonHtml(rowCount = 4) {
return buildGridSkeletonCards(rowCount);
}
export function buildUnloadingGroupsSkeletonHtml(rowCount = 3) {
const items = Array.from({ length: rowCount }, () =>
`${GROUP_ITEM_INNER}
`
).join("");
return `${items}
`;
}
export function buildDuplicatePanelSkeletonHtml() {
return ``;
}
export function buildRecipeIngredientsSkeletonHtml(rowCount = 6) {
const rows = Array.from({ length: rowCount }, () =>
``
).join("");
return `${rows}
`;
}
function prefersReducedMotion() {
return globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;
}
function isGridSkeletonHost(container) {
if (!container) return false;
return (
container.getAttribute("aria-busy") === "true" &&
container.querySelector(":scope > .kiosk-skeleton-card") != null
);
}
function findSkeletonRoot(container) {
if (!container) return null;
if (isGridSkeletonHost(container)) return container;
return container.querySelector(
":scope > .kiosk-skeleton-groups, :scope > .kiosk-skeleton-table, :scope > .kiosk-skeleton-panel"
);
}
export function mountKioskSkeleton(container, html, label) {
if (!container) return performance.now();
container.innerHTML = html;
container.classList.remove("kiosk-skeleton-host--exit");
if (container.querySelector(":scope > .kiosk-skeleton-card")) {
container.setAttribute("aria-busy", "true");
if (label) container.setAttribute("aria-label", label);
}
return performance.now();
}
export async function awaitKioskSkeletonMin(startedAt, minMs = KIOSK_SKELETON_MIN_MS) {
const elapsed = performance.now() - startedAt;
const rest = minMs - elapsed;
if (rest > 0) {
await new Promise((resolve) => setTimeout(resolve, rest));
}
}
async function fadeOutSkeleton(skel) {
if (!skel || prefersReducedMotion()) return;
const exitClass = isGridSkeletonHost(skel)
? "kiosk-skeleton-host--exit"
: skel.classList.contains("kiosk-skeleton-groups")
? "kiosk-skeleton-groups--exit"
: skel.classList.contains("kiosk-skeleton-table")
? "kiosk-skeleton-table--exit"
: "kiosk-skeleton-panel--exit";
skel.classList.add(exitClass);
await new Promise((resolve) => {
let settled = false;
const finish = () => {
if (settled) return;
settled = true;
skel.removeEventListener("transitionend", onEnd);
resolve();
};
const onEnd = (event) => {
if (event.target === skel && event.propertyName === "opacity") finish();
};
skel.addEventListener("transitionend", onEnd);
globalThis.setTimeout(finish, KIOSK_SKELETON_EXIT_MS + 40);
});
}
function clearSkeletonHostAttrs(container) {
if (!container) return;
container.classList.remove("kiosk-skeleton-host--exit");
container.removeAttribute("aria-busy");
container.removeAttribute("aria-label");
}
export async function replaceKioskContent(container, html, options = {}) {
if (!container) return;
const { minMs = KIOSK_SKELETON_MIN_MS, startedAt = performance.now() } = options;
const skel = findSkeletonRoot(container);
if (skel) {
await awaitKioskSkeletonMin(startedAt, minMs);
await fadeOutSkeleton(skel);
} else {
await awaitKioskSkeletonMin(startedAt, minMs);
}
container.innerHTML = html;
clearSkeletonHostAttrs(container);
}
export function clearStaleKioskSkeleton(container) {
if (!container) return;
if (findSkeletonRoot(container)) {
container.innerHTML = "";
clearSkeletonHostAttrs(container);
}
}
export function mountDuplicatePanelSkeleton(recipeInfoEl, weightSectionEl) {
const html = buildDuplicatePanelSkeletonHtml();
const startedAt = performance.now();
if (recipeInfoEl) {
recipeInfoEl.style.display = "block";
recipeInfoEl.innerHTML = html;
}
if (weightSectionEl) {
weightSectionEl.classList.add("kiosk-skeleton-host");
const host = weightSectionEl.querySelector(".weight-content");
if (host) {
host.dataset.kioskSkeletonBackup = host.innerHTML;
host.innerHTML = ``;
}
}
return startedAt;
}
export function clearDuplicatePanelSkeleton(recipeInfoEl, weightSectionEl) {
if (recipeInfoEl) {
const skel = recipeInfoEl.querySelector(".kiosk-skeleton-panel");
if (skel) {
recipeInfoEl.style.display = "none";
recipeInfoEl.innerHTML = `
Рецепт:
Компонент 0 из 0
`;
}
}
if (weightSectionEl) {
weightSectionEl.classList.remove("kiosk-skeleton-host");
const host = weightSectionEl.querySelector(".weight-content");
if (host?.dataset.kioskSkeletonBackup) {
host.innerHTML = host.dataset.kioskSkeletonBackup;
delete host.dataset.kioskSkeletonBackup;
}
}
}
const api = {
buildGridSkeletonCards,
buildDispenserGridSkeletonHtml,
buildPeriodRecipeGridSkeletonHtml,
buildUnloadingGroupsSkeletonHtml,
buildDuplicatePanelSkeletonHtml,
buildRecipeIngredientsSkeletonHtml,
mountKioskSkeleton,
replaceKioskContent,
awaitKioskSkeletonMin,
clearStaleKioskSkeleton,
mountDuplicatePanelSkeleton,
clearDuplicatePanelSkeleton,
};
if (typeof globalThis !== "undefined") {
globalThis.WespKioskSkeleton = api;
}