jalil.arfaoui.net/src/components/photo/PhotoGallery.astro
Jalil Arfaoui 3d23e84b34 Internationalisation complète et ajout des pages code, théâtre, acting (FR, EN, AR)
Ajout des pages code et théâtre/acting en FR, EN et AR.
Création de vraies routes localisées /en/photo et /ar/تصوير au lieu du hack ?lang=. Extraction de composants partagés (PhotoHomeContent, PhotoBlogIndexContent, PhotoBlogPostContent, PhotoAlbumContent) pour éviter la duplication entre langues. Traduction des catégories photo (16 fichiers JSON), de la navigation, du footer et des aria-labels.
Routes AR avec slugs arabes (/ar/تصوير/مدونة, /ar/تصوير/ألبومات).
2026-02-18 16:12:53 +01:00

263 lines
No EOL
7.3 KiB
Text

---
import CategoryNav from './CategoryNav.astro';
import Slideshow from './Slideshow.astro';
import SlideControls from './SlideControls.astro';
import favorites from '../../data/favorites.json';
import type { Locale } from '../../utils/i18n';
interface Props {
category?: string;
lang?: Locale;
}
const { lang = 'fr' } = Astro.props;
// Auto-détection des images
const allImages = import.meta.glob<{ default: ImageMetadata }>('/src/assets/images/photos/categories/**/*.{jpg,jpeg,png,webp}');
// Charger les images favorites
const favoriteImages = await Promise.all(
favorites.map(async (relativePath: string) => {
const fullPath = `/src/assets/images/photos/categories/${relativePath}`;
const loader = allImages[fullPath];
if (!loader) {
console.warn(`Image favorite non trouvée: ${fullPath}`);
return null;
}
const img = await loader();
const filename = relativePath.split('/').pop() || '';
const title = filename
.replace(/\.[^/.]+$/, '')
.replace(/-/g, ' ')
.replace(/_/g, ' ');
return {
src: img.default, // ImageMetadata pour <Image>
alt: title,
title: title,
category: relativePath.split('/')[0]
};
})
);
// Filtrer les nulls (images non trouvées)
const images = favoriteImages.filter(img => img !== null);
// Préparation des données pour JavaScript (avec URL au lieu de ImageMetadata)
const imagesForJS = JSON.stringify(images.map(img => ({
src: img.src.src, // Extraire l'URL de ImageMetadata
alt: img.alt,
title: img.title,
category: img.category
})));
---
<div id="photo-gallery" class="gallery-container">
<CategoryNav currentCategory="" lang={lang} />
<Slideshow images={images} lang={lang} />
<SlideControls lang={lang} />
</div>
<script is:inline define:vars={{ imagesForJS }}>
window.galleryData = {
images: JSON.parse(imagesForJS),
currentCategory: 'favorites'
};
// PhotoGallerySlideshow controller
class PhotoGallerySlideshow {
constructor() {
this.images = window.galleryData?.images || [];
this.currentIndex = 0;
this.isPlaying = true;
this.autoplayInterval = null;
this.transitionDuration = 800;
this.autoplaySpeed = 5000;
this.init();
}
init() {
if (this.images.length === 0) {
console.warn('Aucune image trouvée dans galleryData');
return;
}
this.bindEvents();
this.startAutoplay();
this.preloadNextImages();
this.updatePlayPauseButton();
}
bindEvents() {
document.getElementById('prev-btn')?.addEventListener('click', () => this.prevSlide());
document.getElementById('next-btn')?.addEventListener('click', () => this.nextSlide());
document.getElementById('play-pause-btn')?.addEventListener('click', () => this.toggleAutoplay());
document.querySelectorAll('.indicator').forEach((btn, index) => {
btn.addEventListener('click', () => this.goToSlide(index));
});
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
this.prevSlide();
break;
case 'ArrowRight':
e.preventDefault();
this.nextSlide();
break;
case ' ':
e.preventDefault();
this.toggleAutoplay();
break;
case 'Escape':
if (window.history.length > 1) {
history.back();
} else {
window.location.href = '/';
}
break;
}
});
this.setupTouchEvents();
if (!this.isMobile()) {
const slideshow = document.getElementById('slideshow-container');
slideshow?.addEventListener('mouseenter', () => this.pauseAutoplay());
slideshow?.addEventListener('mouseleave', () => this.resumeAutoplay());
}
}
setupTouchEvents() {
const slideshow = document.getElementById('slideshow-container');
if (!slideshow) return;
let startX = 0;
let startY = 0;
slideshow.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}, { passive: true });
slideshow.addEventListener('touchend', (e) => {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
const deltaX = startX - endX;
const deltaY = startY - endY;
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
if (deltaX > 0) {
this.nextSlide();
} else {
this.prevSlide();
}
}
}, { passive: true });
}
goToSlide(index) {
if (index === this.currentIndex || index < 0 || index >= this.images.length) return;
const currentSlide = document.querySelector('.slide.active');
const nextSlide = document.querySelector(`[data-index="${index}"]`);
const currentIndicator = document.querySelector('.indicator.active');
const nextIndicator = document.querySelector(`[data-slide="${index}"]`);
if (currentSlide && nextSlide) {
currentSlide.classList.remove('active');
nextSlide.classList.add('active');
currentIndicator?.classList.remove('active');
nextIndicator?.classList.add('active');
this.currentIndex = index;
this.preloadNextImages();
}
}
nextSlide() {
const nextIndex = (this.currentIndex + 1) % this.images.length;
this.goToSlide(nextIndex);
}
prevSlide() {
const prevIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
this.goToSlide(prevIndex);
}
toggleAutoplay() {
if (this.isPlaying) {
this.pauseAutoplay();
} else {
this.startAutoplay();
}
this.updatePlayPauseButton();
}
startAutoplay() {
if (this.autoplayInterval) return;
this.isPlaying = true;
this.autoplayInterval = setInterval(() => this.nextSlide(), this.autoplaySpeed);
}
pauseAutoplay() {
if (this.autoplayInterval) {
clearInterval(this.autoplayInterval);
this.autoplayInterval = null;
}
this.isPlaying = false;
}
resumeAutoplay() {
if (!this.isPlaying) return;
this.startAutoplay();
}
updatePlayPauseButton() {
const playIcon = document.querySelector('.play-icon');
const pauseIcon = document.querySelector('.pause-icon');
if (this.isPlaying) {
playIcon?.classList.add('hidden');
pauseIcon?.classList.remove('hidden');
} else {
playIcon?.classList.remove('hidden');
pauseIcon?.classList.add('hidden');
}
}
preloadNextImages() {
for (let i = 1; i <= 2; i++) {
const nextIndex = (this.currentIndex + i) % this.images.length;
const nextImage = this.images[nextIndex];
if (nextImage && !document.querySelector(`img[src="${nextImage.src}"]`)) {
const img = new Image();
img.src = nextImage.src;
}
}
}
isMobile() {
return window.innerWidth <= 768;
}
}
document.addEventListener('DOMContentLoaded', () => {
new PhotoGallerySlideshow();
});
</script>
<style>
.gallery-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
background: #000000;
color: #ffffff;
}
</style>