document.addEventListener('DOMContentLoaded', () => { // ---------- DOM Elements ---------- const shell = document.getElementById("pexpo-core-shell"); const topbar = document.getElementById("pexpo-core-topbar"); const filterWrap = document.getElementById("pexpo-core-filterWrap"); const filterBtn = document.getElementById("pexpo-core-filterBtn"); const filterDrawer = document.getElementById("pexpo-core-filterDrawer"); const drawerBackdrop = document.getElementById("pexpo-core-drawerBackdrop"); // const filterBody = document.getElementById("pexpo-core-filterBody"); // const filterLabel = document.getElementById("pexpo-core-filterLabel"); const filterFTag = document.getElementById("pexpo-core-tag-toggle"); const filterTags = document.getElementById("pexpo-core-tags"); const qInput = document.getElementById("pexpo-core-q"); const masonry = document.getElementById("pexpo-core-masonry"); const measure = document.getElementById("pexpo-core-measure"); const count = document.getElementById("pexpo-core-count"); const layoutMeta = document.getElementById("pexpo-core-layoutMeta"); const sortSelect = document.getElementById("pexpo-core-sortSelect"); // ---------- State ---------- let filters = { q: "", sort: "relevance", force_tags: false, tags: [], resultsPerPage: 20, page: 1, }; let activeSortKey = "relevance"; let results = []; // ---------- Initialize MultiSelect Library ---------- if (typeof MultiSelect !== 'undefined') { new MultiSelect(filterTags, { // max: 20, search: true, selectAll: false, onSelect: function(value) { if (!filters.tags.includes(value)) { filters.tags.push(value); } console.log('Updated filters.tags:', filters.tags); }, onUnselect: function(value) { filters.tags = filters.tags.filter(tag => tag !== value); console.log('Updated filters.tags:', filters.tags); } }); } else { console.warn("MultiSelect library not found. Filters will not work without it."); } // ---------- Sort Definitions ---------- const SORTS = { relevance: "Relevánsság (legjobb → legrosszabb)", date_desc: "Dátum (új → régi)", date_asc: "Dátum (régi → új)", title_asc: "Cím (A → Z)", title_desc: "Cím (Z → A)", }; // ---------- Helpers ---------- function escapeHtml(str) { return String(str).replace(/[&<>"']/g, s => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[s])); } function encodeDataToURL(data) { return Object .keys(data) .map(value => `${value}=${encodeURIComponent(data[value])}`) .join('&'); } function cardEl(r) { const div = document.createElement("div"); div.className = "pexpo-core-card"; div.innerHTML = `
${escapeHtml(r.title)}
${escapeHtml(r.title)}
${escapeHtml(r.excerpt)}
${ r.tag != null ? r.tag.map(element => `${escapeHtml(element)}`).join('') : '' }
${escapeHtml(r.date)}
`; return div; } // ---------- Ajax Function ---------- async function fetchData() { // 1. Show loading state if needed masonry.style.opacity = "0.5"; try { const response = await fetch(`/wp-json/pexpo/v1/query?${encodeDataToURL(filters)}`); const data = await response.json(); results = data; requestLayout(); } catch (error) { console.error("Search failed:", error); masonry.innerHTML = `
Hiba történt a keresés során.
`; } finally { masonry.style.opacity = "1"; } } // ---------- Layout & Masonry Logic ---------- const minCard = () => Number(getComputedStyle(document.documentElement).getPropertyValue("--cardMin")) || 240; const gap = () => Number(getComputedStyle(document.documentElement).getPropertyValue("--gap")) || 12; let layoutQueued = false; function requestLayout() { if (layoutQueued) return; layoutQueued = true; requestAnimationFrame(() => { layoutQueued = false; applyMasonry(); }); } function getColumnCount() { const w = masonry.clientWidth || masonry.getBoundingClientRect().width || 1; const mc = minCard(); const g = gap(); return Math.max(1, Math.floor((w + g) / (mc + g))); } function measureCardHeights(cols, list) { const w = masonry.clientWidth || 1; const g = gap(); const colW = Math.floor((w - (cols - 1) * g) / cols); measure.style.width = colW + "px"; measure.innerHTML = ""; const measured = list.map((r, idx) => { const el = cardEl(r); measure.appendChild(el); const h = el.offsetHeight; measure.removeChild(el); return { r, idx, h }; }); return { measured, colW }; } function packRankAware(measured, cols, K = 6) { const colHeights = new Array(cols).fill(0); const placements = []; const queue = measured.slice(); while (queue.length) { // Find shortest column let bestCol = 0; for (let c = 1; c < cols; c++) { if (colHeights[c] < colHeights[bestCol]) bestCol = c; } // Look at top K items, pick the one that fits best (tallest) // or just the next one if you prefer strict ordering. // Here we pick the tallest of the next K to fill gaps. const lookN = Math.min(K, queue.length); let pick = 0; for (let i = 1; i < lookN; i++) { if (queue[i].h > queue[pick].h) pick = i; } const picked = queue.splice(pick, 1)[0]; placements.push({ item: picked, col: bestCol }); colHeights[bestCol] += picked.h + gap(); } return placements; } function applyMasonry() { const n = results.length || 0; count.textContent = n; masonry.innerHTML = ""; const cols = getColumnCount(); // Create Columns with pexpo-core class const colEls = []; for (let c = 0; c < cols; c++) { const col = document.createElement("div"); col.className = "pexpo-core-mCol"; masonry.appendChild(col); colEls.push(col); } const sliced = results.slice(); // Measure & Pack const { measured } = measureCardHeights(cols, sliced); const placements = packRankAware(measured, cols, 6); // Render for (const p of placements) { colEls[p.col].appendChild(cardEl(p.item.r)); } // layoutMeta.textContent = `${cols} oszlop • ${SORTS[activeSortKey]?.label || "Relevance"}`; } // ---------- UI Events ---------- // Sort Dropdown if (sortSelect) { sortSelect.innerHTML = Object.entries(SORTS) .map(([k, label]) => ``) .join(""); sortSelect.value = activeSortKey; sortSelect.addEventListener("change", () => { activeSortKey = sortSelect.value; filters.sort = activeSortKey; fetchData(); }); } // Search qInput.addEventListener("keydown", (e) => { if (e.key === "Enter") { filters.q = qInput.value.trim(); fetchData(); } }); // Tag Toggle filterFTag.addEventListener("change", () => { filters.force_tags = filterFTag.checked; }); // Tags multiselect filterTags.addEventListener("change", (value, text, element) => { console.log('Change:', value, text, element); }); // Filter Drawer Logic function syncTopbarHeight() { const h = topbar.offsetHeight; document.documentElement.style.setProperty("--topbarH", h + "px"); } function setOpen(open) { filterWrap.classList.toggle("pexpo-core-open", open); // Updated class filterBtn.setAttribute("aria-expanded", String(open)); filterDrawer.classList.toggle("pexpo-core-open", open); // Updated class filterDrawer.setAttribute("aria-hidden", String(!open)); drawerBackdrop.classList.toggle("pexpo-core-show", open); // Updated class shell.classList.toggle("pexpo-core-drawerOpen", open); syncTopbarHeight(); } filterBtn.addEventListener("click", () => setOpen(!filterDrawer.classList.contains("pexpo-core-open"))); drawerBackdrop.addEventListener("click", () => setOpen(false)); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && filterDrawer.classList.contains("pexpo-core-open")) setOpen(false); }); // // Apply JSON Filter // function applyJson() { // try { // const parsed = JSON.parse(filterJson.value); // jsonError.classList.remove("pexpo-core-show"); // filters = parsed; // filterLabel.textContent = (filters.category || "Custom"); // // Trigger new search with new filters // filters.q = qInput.value.trim(); // fetchData(); // // Close drawer on success (optional) // // setOpen(false); // } catch (err) { // jsonError.classList.add("pexpo-core-show"); // } // } // filterJson.addEventListener("keydown", (e) => { // if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { // e.preventDefault(); // applyJson(); // } // }); // Resize Observer const ro = new ResizeObserver(() => { syncTopbarHeight(); requestLayout(); }); ro.observe(shell); ro.observe(masonry); // Initial Load initFilters(); syncTopbarHeight(); fetchData(); // --- Temporary Mock Response Generator (DELETE ME IN PRODUCTION) --- function simulateBackendResponse(q) { const count = filters.maxResults || 12; const arr = []; for(let i=0; i