feat(blog): in-content image carousel/slider
Editor: new 🎠 اسلایدر toolbar button — pick multiple images (min 2), uploads them all, inserts a <div class="post-carousel" data-carousel> block at the cursor. Editor preview shows a tidy filmstrip with the non-functional arrows/dots hidden. Public post page: carousel CSS (scroll-snap track) + JS that wires up prev/next arrows, clickable dots, and native touch swipe. Single-image blocks auto-collapse their controls. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,18 @@
|
||||
.article-content a{color:var(--gold);border-bottom:1px solid var(--gold-pale)}
|
||||
.article-content blockquote{border-right:4px solid var(--gold);padding:.8rem 1.2rem;background:var(--gold-pale);border-radius:0 8px 8px 0;margin:1.2rem 0;font-style:italic;color:var(--mid)}
|
||||
.article-content img{max-width:100%;height:auto;border-radius:12px;margin:1.2rem 0;display:block}
|
||||
/* ─── In-content image carousel ───────────────────────────────── */
|
||||
.post-carousel{position:relative;margin:1.5rem 0;border-radius:14px;overflow:hidden;background:#111}
|
||||
.post-carousel .pc-track{display:flex;direction:ltr;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scrollbar-width:none}
|
||||
.post-carousel .pc-track::-webkit-scrollbar{display:none}
|
||||
.post-carousel .pc-track img{flex:0 0 100%;width:100%;max-height:480px;object-fit:cover;scroll-snap-align:center;display:block;margin:0 !important;border-radius:0 !important}
|
||||
.post-carousel .pc-prev,.post-carousel .pc-next{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.45);color:#fff;border:none;width:40px;height:40px;border-radius:50%;font-size:1.5rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:2;transition:background .2s}
|
||||
.post-carousel .pc-prev:hover,.post-carousel .pc-next:hover{background:rgba(0,0,0,.72)}
|
||||
.post-carousel .pc-prev{left:10px}
|
||||
.post-carousel .pc-next{right:10px}
|
||||
.post-carousel .pc-dots{position:absolute;bottom:10px;left:0;right:0;display:flex;gap:6px;justify-content:center;z-index:2}
|
||||
.post-carousel .pc-dots span{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.5);cursor:pointer;transition:background .2s}
|
||||
.post-carousel .pc-dots span.active{background:#fff;width:20px;border-radius:4px}
|
||||
/* ─── Tags ─────────────────────────────────────────────────────── */
|
||||
.article-tags{margin-top:2rem;padding-top:1.5rem;border-top:1px solid var(--border);display:flex;gap:.5rem;flex-wrap:wrap}
|
||||
.tag{background:var(--bg);border:1px solid var(--border);padding:.25rem .75rem;border-radius:50px;font-size:.78rem;color:var(--mid)}
|
||||
@@ -300,3 +312,31 @@
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
// Initialise any in-content image carousels (swipe + arrows + dots)
|
||||
document.querySelectorAll('[data-carousel]').forEach(c => {
|
||||
const track = c.querySelector('.pc-track');
|
||||
if (!track) return;
|
||||
const imgs = [...track.querySelectorAll('img')];
|
||||
const dots = c.querySelector('.pc-dots');
|
||||
if (imgs.length <= 1) {
|
||||
c.querySelector('.pc-prev')?.remove();
|
||||
c.querySelector('.pc-next')?.remove();
|
||||
dots?.remove();
|
||||
return;
|
||||
}
|
||||
if (dots) dots.innerHTML = imgs.map((_, i) => `<span data-i="${i}"></span>`).join('');
|
||||
const dotEls = dots ? [...dots.querySelectorAll('span')] : [];
|
||||
const current = () => Math.round(track.scrollLeft / track.clientWidth);
|
||||
const update = () => { const i = current(); dotEls.forEach((d, di) => d.classList.toggle('active', di === i)); };
|
||||
const go = (i) => { i = Math.max(0, Math.min(imgs.length - 1, i)); track.scrollTo({ left: i * track.clientWidth, behavior: 'smooth' }); };
|
||||
c.querySelector('.pc-prev')?.addEventListener('click', () => go(current() - 1));
|
||||
c.querySelector('.pc-next')?.addEventListener('click', () => go(current() + 1));
|
||||
dotEls.forEach((d, di) => d.addEventListener('click', () => go(di)));
|
||||
track.addEventListener('scroll', () => { clearTimeout(track._t); track._t = setTimeout(update, 60); });
|
||||
update();
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user