In-post event gallery block + lightbox; mobile polish

- Reusable "Photos from the event" grid (shows 5 thumbnails + "+N more") that
  opens a full-screen lightbox (arrows / keyboard / swipe / Esc); added to the
  Meet Student Slovenia 2024 post as the pattern
- New css/components/_lightbox.css + setupLightbox() in main.js (injects one
  shared overlay, wires any .js-lightbox grid)
- Mobile polish: gallery albums go single-column on phones, two on tablets;
  event-gallery grid 3-up on mobile; lightbox sizing/controls tuned for small
  screens

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
popovskikandClaude Opus 4.8 committed 2026-08-03 15:16:28 +02:00
1 parent 24c77e6171
commit 1f5e031be9
5 files changed
+183 -6

No files matched your search

+76
View File
@@ -833,6 +833,81 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
/**
* ===================================================================
* REUSABLE LIGHTBOX (for .event-gallery-grid.js-lightbox blocks)
* ===================================================================
*/
function setupLightbox() {
var grids = document.querySelectorAll('.js-lightbox');
if (!grids.length) return;
var lb = document.createElement('div');
lb.className = 'lightbox';
lb.setAttribute('role', 'dialog');
lb.setAttribute('aria-label', 'Photo viewer');
lb.setAttribute('aria-hidden', 'true');
lb.innerHTML =
'<button type="button" class="lightbox-close" aria-label="Close">&times;</button>' +
'<button type="button" class="lightbox-prev" aria-label="Previous">&#10094;</button>' +
'<img class="lightbox-img" src="" alt="">' +
'<button type="button" class="lightbox-next" aria-label="Next">&#10095;</button>' +
'<div class="lightbox-caption"></div>';
document.body.appendChild(lb);
var img = lb.querySelector('.lightbox-img');
var cap = lb.querySelector('.lightbox-caption');
var group = [];
var idx = 0;
function render() {
var it = group[idx];
img.setAttribute('src', it.full);
img.setAttribute('alt', it.caption || '');
cap.textContent = (it.caption || '') + (group.length > 1 ? ' (' + (idx + 1) + '/' + group.length + ')' : '');
}
function open(grid, start) {
group = Array.prototype.map.call(grid.querySelectorAll('.eg-thumb'), function (b) {
return { full: b.getAttribute('data-full'), caption: b.getAttribute('data-caption') || '' };
});
idx = start;
render();
lb.classList.add('is-open');
lb.setAttribute('aria-hidden', 'false');
document.documentElement.style.overflow = 'hidden';
}
function close() {
lb.classList.remove('is-open');
lb.setAttribute('aria-hidden', 'true');
document.documentElement.style.overflow = '';
img.setAttribute('src', '');
}
function step(d) { idx = (idx + d + group.length) % group.length; render(); }
grids.forEach(function (grid) {
grid.querySelectorAll('.eg-thumb').forEach(function (b, i) {
b.addEventListener('click', function () { open(grid, i); });
});
});
lb.querySelector('.lightbox-close').addEventListener('click', close);
lb.querySelector('.lightbox-prev').addEventListener('click', function () { step(-1); });
lb.querySelector('.lightbox-next').addEventListener('click', function () { step(1); });
lb.addEventListener('click', function (e) { if (e.target === lb) close(); });
document.addEventListener('keydown', function (e) {
if (!lb.classList.contains('is-open')) return;
if (e.key === 'Escape') close();
else if (e.key === 'ArrowLeft') step(-1);
else if (e.key === 'ArrowRight') step(1);
});
// Swipe on touch devices
var sx = 0;
lb.addEventListener('touchstart', function (e) { sx = e.changedTouches[0].clientX; }, { passive: true });
lb.addEventListener('touchend', function (e) {
var dx = e.changedTouches[0].clientX - sx;
if (Math.abs(dx) > 40) step(dx < 0 ? 1 : -1);
}, { passive: true });
}
/**
* ===================================================================
* HUB USE-CASE CAROUSEL (auto-scrolling)
@@ -863,5 +938,6 @@ document.addEventListener('DOMContentLoaded', function() {
setupLanguageSuggestion();
setupNewsletter();
setupHubCarousel();
setupLightbox();
});