import { MENU_ASSET_VERSION } from "./config.js";
import { trackEvent } from "./analytics.js";
let itemModalKeys = [];
let itemModalIndex = -1;
let itemModalTouchStart = null;
let itemModalDragging = false;
let itemModalAnimating = false;
function resetItemModalPane() {
const pane = document.getElementById("itemModalPane");
if (!pane) return;
pane.classList.remove(
"is-dragging",
"is-exiting-left",
"is-exiting-right",
"is-entering-from-left",
"is-entering-from-right"
);
pane.style.transform = "";
pane.style.opacity = "";
}
function waitForPaneTransition(pane, timeoutMs = 400) {
return new Promise((resolve) => {
let settled = false;
const finish = () => {
if (settled) return;
settled = true;
pane.removeEventListener("transitionend", onEnd);
resolve();
};
const onEnd = (e) => {
if (e.target === pane) finish();
};
pane.addEventListener("transitionend", onEnd);
setTimeout(finish, timeoutMs);
});
}
function isValidMenuImageUrl(url) {
return typeof url === "string" && url.trim().length > 0;
}
export function handleMenuImageError(imgEl) {
if (!imgEl) return;
imgEl.onerror = null;
imgEl.classList.add("hidden");
const wrap = imgEl.closest(".rmc-image-wrap, .item-modal-image-wrap");
const placeholder = wrap?.querySelector(".menu-image-placeholder");
if (placeholder) placeholder.classList.remove("hidden");
}
function applyMenuItemImage(imgEl, placeholderEl, url) {
if (!imgEl || !placeholderEl) return;
if (!isValidMenuImageUrl(url)) {
imgEl.src = "";
imgEl.classList.add("hidden");
placeholderEl.classList.remove("hidden");
return;
}
imgEl.onload = () => {
imgEl.classList.remove("hidden");
placeholderEl.classList.add("hidden");
};
imgEl.onerror = () => handleMenuImageError(imgEl);
imgEl.classList.remove("hidden");
placeholderEl.classList.add("hidden");
imgEl.src = url;
}
function renderMenuListImage(url) {
const hasUrl = isValidMenuImageUrl(url);
const imgClass = hasUrl ? "rmc-image" : "rmc-image hidden";
const placeholderClass = hasUrl ? "menu-image-placeholder hidden" : "menu-image-placeholder";
const srcAttr = hasUrl ? ` src="${url}"` : "";
const onerror = hasUrl ? ' onerror="handleMenuImageError(this)"' : "";
return `
![]()
`;
}
function findMenuItem(categoryId, position) {
if (!window.menuDataRaw) return null;
for (const cat of window.menuDataRaw) {
for (const item of cat.items) {
if (item.categoryId == categoryId && item.position == position) {
return item;
}
}
}
return null;
}
function buildVisibleMenuItemKeys() {
const keys = [];
document.querySelectorAll(".rmc-position").forEach((el) => {
if (el.style.display === "none") return;
const category = el.closest(".rm-category");
if (category && category.style.display === "none") return;
keys.push({
categoryId: el.getAttribute("data-category-id"),
position: el.getAttribute("data-position"),
});
});
return keys;
}
function updateItemModalNavigation() {
const total = itemModalKeys.length;
const counter = document.getElementById("itemModalCounter");
if (counter) counter.textContent = total > 1 ? `${itemModalIndex + 1} / ${total}` : "";
}
function populateItemModal(item) {
const imgEl = document.getElementById("itemModalImage");
const placeholderEl = document.getElementById("itemModalImagePlaceholder");
const titleEl = document.getElementById("itemModalTitle");
const descEl = document.getElementById("itemModalDesc");
const priceEl = document.getElementById("itemModalPrice");
applyMenuItemImage(imgEl, placeholderEl, item.image);
if (titleEl) titleEl.textContent = item.title;
if (descEl) descEl.textContent = item.description || "";
if (priceEl) priceEl.textContent = item.price;
updateItemModalNavigation();
}
export async function navigateItemModal(delta) {
if (!itemModalKeys.length || !delta || itemModalAnimating) return;
const nextIndex = itemModalIndex + delta;
if (nextIndex < 0 || nextIndex >= itemModalKeys.length) return;
const key = itemModalKeys[nextIndex];
const item = findMenuItem(key.categoryId, key.position);
const pane = document.getElementById("itemModalPane");
if (!item || !pane) return;
itemModalAnimating = true;
resetItemModalPane();
const exitClass = delta > 0 ? "is-exiting-left" : "is-exiting-right";
const enterClass = delta > 0 ? "is-entering-from-right" : "is-entering-from-left";
pane.classList.add(exitClass);
await waitForPaneTransition(pane);
itemModalIndex = nextIndex;
populateItemModal(item);
pane.classList.remove(exitClass);
pane.classList.add(enterClass);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
pane.classList.remove(enterClass);
});
});
await waitForPaneTransition(pane);
itemModalAnimating = false;
}
export function bindItemModalSwipe() {
const content = document.getElementById("itemModalContent");
const pane = document.getElementById("itemModalPane");
const modal = document.getElementById("itemModal");
if (!content || !pane || !modal || content.dataset.swipeBound) return;
content.dataset.swipeBound = "1";
content.addEventListener(
"touchstart",
(e) => {
if (itemModalAnimating || itemModalKeys.length <= 1 || e.touches.length !== 1) return;
if (e.target.closest("button")) return;
const touch = e.touches[0];
itemModalTouchStart = { x: touch.clientX, y: touch.clientY };
itemModalDragging = false;
},
{ passive: true }
);
content.addEventListener(
"touchmove",
(e) => {
if (!itemModalTouchStart || itemModalAnimating || itemModalKeys.length <= 1) return;
const touch = e.touches[0];
const dx = touch.clientX - itemModalTouchStart.x;
const dy = touch.clientY - itemModalTouchStart.y;
if (!itemModalDragging) {
if (Math.abs(dx) > 12 && Math.abs(dx) > Math.abs(dy)) {
itemModalDragging = true;
pane.classList.add("is-dragging");
} else if (Math.abs(dy) > 12) {
itemModalTouchStart = null;
return;
} else {
return;
}
}
let translateX = dx;
if (itemModalIndex <= 0 && dx > 0) translateX = dx * 0.3;
if (itemModalIndex >= itemModalKeys.length - 1 && dx < 0) translateX = dx * 0.3;
pane.style.transform = `translateX(${translateX}px)`;
pane.style.opacity = String(1 - Math.min(Math.abs(translateX) / 320, 0.18));
},
{ passive: true }
);
content.addEventListener(
"touchend",
(e) => {
if (!itemModalTouchStart) return;
const touch = e.changedTouches[0];
const dx = touch.clientX - itemModalTouchStart.x;
const dy = touch.clientY - itemModalTouchStart.y;
const wasDragging = itemModalDragging;
itemModalTouchStart = null;
itemModalDragging = false;
if (!wasDragging) return;
pane.classList.remove("is-dragging");
pane.style.transform = "";
pane.style.opacity = "";
if (Math.abs(dx) >= 50 && Math.abs(dx) > Math.abs(dy)) {
navigateItemModal(dx < 0 ? 1 : -1);
}
},
{ passive: true }
);
document.addEventListener("keydown", (e) => {
if (!modal.classList.contains("active")) return;
if (e.key === "ArrowRight") navigateItemModal(1);
if (e.key === "ArrowLeft") navigateItemModal(-1);
if (e.key === "Escape") closeItemModal();
});
}
export async function loadMenu() {
try {
const response = await fetch(`menu.json?v=${encodeURIComponent(MENU_ASSET_VERSION)}`);
if (!response.ok) throw new Error("Nie udało się załadować menu");
const menuData = await response.json();
window.menuDataRaw = menuData;
const container = document.getElementById("menuContainer");
if (!container) return;
container.innerHTML = "";
menuData.forEach((category) => {
const catId = category.items.length > 0 ? category.items[0].categoryId : "";
const catDiv = document.createElement("div");
catDiv.className = "rm-category";
catDiv.setAttribute("data-cat-id", catId);
let html = `
`;
category.items.forEach((item) => {
html += `
${renderMenuListImage(item.image)}
${item.title}${item.description}
${item.price}
`;
});
html += `
`;
catDiv.innerHTML = html;
container.appendChild(catDiv);
});
} catch (err) {
console.error("Błąd ładowania menu:", err);
const container = document.getElementById("menuContainer");
if (container) {
container.innerHTML =
'Nie udało się załadować menu.
';
}
}
}
export function openItemModal(categoryId, position) {
itemModalKeys = buildVisibleMenuItemKeys();
itemModalIndex = itemModalKeys.findIndex(
(key) => key.categoryId == categoryId && key.position == position
);
if (itemModalIndex < 0) {
itemModalKeys = [{ categoryId, position }];
itemModalIndex = 0;
}
const foundItem = findMenuItem(categoryId, position);
if (!foundItem) return;
populateItemModal(foundItem);
resetItemModalPane();
const modal = document.getElementById("itemModal");
if (modal) {
modal.classList.add("active");
document.body.style.overflow = "hidden";
}
}
export function closeItemModal() {
const modal = document.getElementById("itemModal");
if (modal) {
modal.classList.remove("active");
document.body.style.overflow = "";
}
itemModalTouchStart = null;
itemModalDragging = false;
itemModalAnimating = false;
resetItemModalPane();
}
export function filterMenu() {
const query = document.getElementById("menuSearchInput").value.toLowerCase();
const now = Date.now();
if (query.length >= 2 && (!window.lastMenuSearchEventAt || now - window.lastMenuSearchEventAt > 8000)) {
window.lastMenuSearchEventAt = now;
trackEvent("menu_search", { queryLength: query.length });
}
const categories = document.querySelectorAll(".rm-category");
categories.forEach((category) => {
let hasVisibleItems = false;
const items = category.querySelectorAll(".rmc-position");
items.forEach((item) => {
const title = item.querySelector(".rmc-title h4").textContent.toLowerCase();
if (title.includes(query)) {
item.style.display = "";
hasVisibleItems = true;
} else {
item.style.display = "none";
}
});
if (hasVisibleItems) {
category.style.display = "";
} else {
category.style.display = "none";
}
});
}
export function showCategory(categoryId) {
document.querySelectorAll(".menu-categories-nav a").forEach((a) => a.classList.remove("active"));
const clickedLink = document.querySelector(`.menu-categories-nav a[data-category-badge="${categoryId}"]`);
if (clickedLink) {
clickedLink.classList.add("active");
clickedLink.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
}
const categories = document.querySelectorAll(".rm-category");
categories.forEach((cat) => {
if (categoryId === 0) {
cat.style.display = "";
} else {
const catId = parseInt(cat.getAttribute("data-cat-id"), 10);
if (catId === categoryId) {
cat.style.display = "";
} else {
cat.style.display = "none";
}
}
});
}