add carousel to post overlay

This commit is contained in:
Your Name 2026-01-02 01:51:08 -05:00
parent b6c4f52df2
commit 36fc9a7dd1
2 changed files with 154 additions and 2 deletions

View File

@ -235,14 +235,50 @@ function applyPostDetails(modalWrap, post) {
const summaryEl = modalWrap.querySelector(".sw-modal-summary"); const summaryEl = modalWrap.querySelector(".sw-modal-summary");
if (summaryEl) summaryEl.textContent = summary; if (summaryEl) summaryEl.textContent = summary;
const heroGallery = collectGalleryImages(post);
const heroImageSrc = heroGallery[0] || heroImageForPost(post);
const heroFullSrc = heroGallery[0] || fullImageForPost(post);
const heroHasGallery = heroGallery.length > 1;
const hero = modalWrap.querySelector(".sw-modal-hero"); const hero = modalWrap.querySelector(".sw-modal-hero");
const clusterHero = modalWrap.querySelector(".sw-cluster-hero-media"); const clusterHero = modalWrap.querySelector(".sw-cluster-hero-media");
if (hero || clusterHero) { if (hero || clusterHero) {
const target = hero || clusterHero; const target = hero || clusterHero;
target.innerHTML = img const heroImageHtml = heroImageSrc
? `<img src="${escapeHtml(img)}" data-fullsrc="${escapeHtml(fullImg)}" alt="" loading="lazy" />` ? `<img src="${escapeHtml(heroImageSrc)}" data-fullsrc="${escapeHtml(heroFullSrc)}" alt="" loading="lazy" data-gallery-index="0" />`
: `<div class="sw-modal-hero-fallback"><div class="sw-modal-hero-fallback-inner"><i class="${categoryIconClass(post)}"></i><span>${escapeHtml(normCatLabel(post))}</span></div></div>`; : `<div class="sw-modal-hero-fallback"><div class="sw-modal-hero-fallback-inner"><i class="${categoryIconClass(post)}"></i><span>${escapeHtml(normCatLabel(post))}</span></div></div>`;
const arrowHtml = heroHasGallery
? `<button class="sw-modal-hero-arrow left" type="button" aria-label="Previous image"><i class="fa-solid fa-chevron-left" /></button><button class="sw-modal-hero-arrow right" type="button" aria-label="Next image"><i class="fa-solid fa-chevron-right" /></button>`
: "";
target.innerHTML = `${heroImageHtml}${arrowHtml}`;
let galleryRow = modalWrap.querySelector(".sw-modal-gallery-row");
if (heroHasGallery) {
if (!galleryRow) {
galleryRow = document.createElement("div");
galleryRow.className = "sw-modal-gallery-row";
const heroRow = target.closest(".sw-modal-hero-row");
const parent = heroRow?.parentNode || target.parentNode || modalWrap;
if (parent) {
parent.insertBefore(galleryRow, heroRow ? heroRow.nextSibling : target.nextSibling || null);
} else {
modalWrap.appendChild(galleryRow);
}
}
galleryRow.innerHTML = heroGallery
.map(
(url, idx) =>
`<button type="button" class="sw-modal-gallery-thumb${idx === 0 ? " active" : ""}" data-index="${idx}"><img src="${escapeHtml(
url
)}" alt="" loading="lazy" /></button>`
)
.join("");
} else if (galleryRow) {
galleryRow.remove();
galleryRow = null;
}
bindHeroLightbox(modalWrap); bindHeroLightbox(modalWrap);
if (heroHasGallery) {
initModalGallery(modalWrap, heroGallery);
}
} }
const badge = modalWrap.querySelector(".sw-modal-badge"); const badge = modalWrap.querySelector(".sw-modal-badge");
@ -447,6 +483,44 @@ function bindHeroLightbox(modalWrap) {
}); });
} }
function initModalGallery(modalWrap, gallery) {
if (!modalWrap || !Array.isArray(gallery) || gallery.length === 0) return;
const heroImg = modalWrap.querySelector(".sw-modal-hero img, .sw-cluster-hero-media img");
if (!heroImg) return;
let activeIndex = 0;
const thumbButtons = modalWrap.querySelectorAll(".sw-modal-gallery-thumb");
const arrows = modalWrap.querySelectorAll(".sw-modal-hero-arrow");
const setActive = (idx) => {
if (!Number.isFinite(idx)) return;
const normalized = (idx + gallery.length) % gallery.length;
activeIndex = normalized;
heroImg.src = gallery[normalized];
heroImg.dataset.fullsrc = gallery[normalized];
heroImg.dataset.galleryIndex = String(normalized);
thumbButtons.forEach((btn) => {
const btnIndex = Number(btn.dataset.index);
btn.classList.toggle("active", btnIndex === normalized);
});
};
arrows.forEach((btn) => {
btn.addEventListener("click", (event) => {
event.stopPropagation();
const direction = btn.classList.contains("left") ? -1 : 1;
const nextIndex = (activeIndex + direction + gallery.length) % gallery.length;
setActive(nextIndex);
});
});
thumbButtons.forEach((btn) => {
btn.addEventListener("click", (event) => {
event.stopPropagation();
const idx = Number(btn.dataset.index);
if (Number.isNaN(idx)) return;
setActive(idx);
});
});
setActive(0);
}
function openImageLightbox(src) { function openImageLightbox(src) {
if (!src) return; if (!src) return;
const overlay = document.createElement("div"); const overlay = document.createElement("div");
@ -607,6 +681,25 @@ function fullImageForPost(post) {
return normalizeImageUrl(raw); return normalizeImageUrl(raw);
} }
function collectGalleryImages(post) {
const result = [];
const seen = new Set();
const add = (value) => {
const normalized = normalizeImageUrl(value);
if (!normalized) return;
if (seen.has(normalized)) return;
seen.add(normalized);
result.push(normalized);
};
if (Array.isArray(post?.media_urls)) {
post.media_urls.forEach(add);
}
["image_large", "image_medium", "image_small", "image", "media_url"].forEach((key) => {
add(post?.[key]);
});
return result;
}
function formatRelativeTime(createdAt) { function formatRelativeTime(createdAt) {
try { try {
if (!createdAt) return "now"; if (!createdAt) return "now";

View File

@ -886,6 +886,65 @@ body[data-theme="blue"] .sw-centered-modal .post-card.sw-expanded-shell{
display:block; display:block;
} }
.sw-modal-hero-media{
position:relative;
}
.sw-modal-hero-arrow{
position:absolute;
top:50%;
transform:translateY(-50%) scale(0.95);
width:36px;
height:36px;
border-radius:50%;
border:1px solid rgba(255,255,255,0.35);
background: rgba(0,0,0,0.55);
color:#fff;
display:flex;
align-items:center;
justify-content:center;
cursor:pointer;
z-index:10;
transition:transform 0.2s ease;
}
.sw-modal-hero-arrow:hover{
transform:translateY(-50%) scale(1);
}
.sw-modal-hero-arrow.left{
left:10px;
}
.sw-modal-hero-arrow.right{
right:10px;
}
.sw-modal-gallery-row{
display:flex;
gap:0.35rem;
overflow-x:auto;
padding-top:0.5rem;
margin-top:0.5rem;
}
.sw-modal-gallery-thumb{
border-radius:12px;
border:1px solid transparent;
width:48px;
height:48px;
flex:0 0 auto;
overflow:hidden;
padding:0;
display:inline-flex;
align-items:center;
justify-content:center;
background:rgba(15,23,42,0.65);
}
.sw-modal-gallery-thumb.active{
border-color:rgba(56,189,248,0.95);
}
.sw-modal-gallery-thumb img{
width:100%;
height:100%;
object-fit:cover;
display:block;
}
.sw-modal-hero-fallback{ .sw-modal-hero-fallback{
width:100%; width:100%;
height:100%; height:100%;