| 1 |
// <mnw-carousel> — click-through carousel over server-rendered frames, ported |
| 2 |
// from static/carousel.js. Progressive enhancement: until the element upgrades, |
| 3 |
// the first frame shows and the controls are inert (CSS gates on [data-ready]). |
| 4 |
|
| 5 |
import { MnwElement, define } from './base.ts'; |
| 6 |
import { wrapIndex } from './carousel.logic.ts'; |
| 7 |
|
| 8 |
class Carousel extends MnwElement { |
| 9 |
protected init(): void { |
| 10 |
const frames = Array.from(this.querySelectorAll<HTMLElement>('.carousel-frame')); |
| 11 |
if (frames.length < 2) return; // nothing to navigate |
| 12 |
|
| 13 |
const dots = Array.from(this.querySelectorAll<HTMLElement>('[data-carousel-dot]')); |
| 14 |
let index = frames.findIndex((f) => f.classList.contains('is-active')); |
| 15 |
if (index < 0) index = 0; |
| 16 |
|
| 17 |
const show = (next: number): void => { |
| 18 |
index = wrapIndex(next, frames.length); |
| 19 |
frames.forEach((f, i) => { |
| 20 |
const active = i === index; |
| 21 |
f.classList.toggle('is-active', active); |
| 22 |
if (active) f.removeAttribute('aria-hidden'); |
| 23 |
else f.setAttribute('aria-hidden', 'true'); |
| 24 |
}); |
| 25 |
dots.forEach((d, i) => { |
| 26 |
const active = i === index; |
| 27 |
d.classList.toggle('is-active', active); |
| 28 |
d.setAttribute('aria-selected', active ? 'true' : 'false'); |
| 29 |
}); |
| 30 |
}; |
| 31 |
|
| 32 |
this.querySelector('[data-carousel-prev]')?.addEventListener('click', () => show(index - 1)); |
| 33 |
this.querySelector('[data-carousel-next]')?.addEventListener('click', () => show(index + 1)); |
| 34 |
dots.forEach((d) => { |
| 35 |
d.addEventListener('click', () => show(parseInt(d.getAttribute('data-carousel-dot') ?? '', 10) || 0)); |
| 36 |
}); |
| 37 |
this.addEventListener('keydown', (e) => { |
| 38 |
if (e.key === 'ArrowLeft') { |
| 39 |
show(index - 1); |
| 40 |
e.preventDefault(); |
| 41 |
} else if (e.key === 'ArrowRight') { |
| 42 |
show(index + 1); |
| 43 |
e.preventDefault(); |
| 44 |
} |
| 45 |
}); |
| 46 |
|
| 47 |
this.setAttribute('data-ready', ''); // CSS hook: controls become live |
| 48 |
} |
| 49 |
} |
| 50 |
|
| 51 |
define('mnw-carousel', Carousel); |
| 52 |
|