Ajout de la section galerie photo et nettoyage du template
Galerie photo :
- Ajout du layout photo avec slideshow plein écran
- Navigation par catégories (portraits, paysages, nature, etc.)
- Section "Fil Photo" avec posts illustrés (photoBlogPosts)
- Lightbox pour les albums de catégories
- Composants : Slideshow, CategoryNav, CategoryGrid, Lightbox, MasonryGallery
Nettoyage :
- Suppression du contenu démo du template (posts, images, about)
- Consolidation src/collections/ dans src/data/
- Suppression du config.js dupliqué (garde config.ts)
- Nettoyage des assets inutilisés (posts/, experiences/)
Corrections :
- Favicon récupéré du site actuel
- Chemins favicon corrigés dans les layouts
UI :
- Page d'accueil mise à jour
- Header/Footer simplifiés
- Nouvelle page À propos
2026-01-07 01:45:40 +01:00
|
|
|
---
|
|
|
|
|
import { getCollection } from 'astro:content';
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
images: { src: string; alt: string; title?: string }[];
|
|
|
|
|
albumTitle?: string;
|
|
|
|
|
showCategory?: boolean;
|
|
|
|
|
category?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { images, albumTitle = '', showCategory = false, category = '' } = Astro.props;
|
|
|
|
|
|
|
|
|
|
const imagesForJS = JSON.stringify(images);
|
|
|
|
|
|
|
|
|
|
// Construire les labels depuis la collection
|
|
|
|
|
const photoCategories = await getCollection('photoCategories');
|
|
|
|
|
const categoryLabels: Record<string, string> = {
|
|
|
|
|
'blog': 'Fil Photo',
|
|
|
|
|
...Object.fromEntries(photoCategories.map(cat => [cat.id, cat.data.title]))
|
|
|
|
|
};
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
<div id="lightbox" class="lightbox hidden">
|
|
|
|
|
<div class="lightbox-controls">
|
|
|
|
|
<button class="lightbox-fullscreen" aria-label="Plein écran">
|
|
|
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
|
|
|
|
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="lightbox-close" aria-label="Fermer">
|
|
|
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
|
|
|
|
<path d="M18 6L6 18M6 6l12 12" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button class="lightbox-prev" aria-label="Image précédente">
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
|
|
|
<path d="M15 18L9 12L15 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="lightbox-next" aria-label="Image suivante">
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
|
|
|
<path d="M9 18L15 12L9 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<div class="lightbox-image-container">
|
|
|
|
|
<img id="lightbox-image" class="lightbox-image" src="" alt="" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="lightbox-bottom-bar">
|
|
|
|
|
<div class="lightbox-info">
|
|
|
|
|
<span id="lightbox-title" class="info-title"></span>
|
|
|
|
|
{showCategory && (
|
|
|
|
|
<>
|
|
|
|
|
<span class="info-separator">•</span>
|
|
|
|
|
<span id="lightbox-category" class="info-category"></span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
<span class="info-separator">•</span>
|
|
|
|
|
<span id="lightbox-counter" class="info-counter"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script define:vars={{ imagesForJS, albumTitle, showCategory, category, categoryLabels }}>
|
|
|
|
|
window.lightboxData = {
|
|
|
|
|
images: JSON.parse(imagesForJS),
|
|
|
|
|
albumTitle,
|
|
|
|
|
showCategory,
|
|
|
|
|
category,
|
|
|
|
|
categoryLabels
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
2026-01-07 03:03:42 +01:00
|
|
|
declare global {
|
|
|
|
|
interface Window {
|
|
|
|
|
lightboxData?: {
|
|
|
|
|
images: { src: string; alt: string; title?: string; category?: string }[];
|
|
|
|
|
albumTitle: string;
|
|
|
|
|
showCategory: boolean;
|
|
|
|
|
category: string;
|
|
|
|
|
categoryLabels: Record<string, string>;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Ajout de la section galerie photo et nettoyage du template
Galerie photo :
- Ajout du layout photo avec slideshow plein écran
- Navigation par catégories (portraits, paysages, nature, etc.)
- Section "Fil Photo" avec posts illustrés (photoBlogPosts)
- Lightbox pour les albums de catégories
- Composants : Slideshow, CategoryNav, CategoryGrid, Lightbox, MasonryGallery
Nettoyage :
- Suppression du contenu démo du template (posts, images, about)
- Consolidation src/collections/ dans src/data/
- Suppression du config.js dupliqué (garde config.ts)
- Nettoyage des assets inutilisés (posts/, experiences/)
Corrections :
- Favicon récupéré du site actuel
- Chemins favicon corrigés dans les layouts
UI :
- Page d'accueil mise à jour
- Header/Footer simplifiés
- Nouvelle page À propos
2026-01-07 01:45:40 +01:00
|
|
|
class Lightbox {
|
2026-01-07 03:03:42 +01:00
|
|
|
images: { src: string; alt: string; title?: string; category?: string }[] = [];
|
|
|
|
|
currentIndex = 0;
|
|
|
|
|
lightbox: HTMLElement | null;
|
|
|
|
|
lightboxImage: HTMLImageElement | null;
|
|
|
|
|
lightboxTitle: HTMLElement | null;
|
|
|
|
|
lightboxCategory: HTMLElement | null;
|
|
|
|
|
lightboxCounter: HTMLElement | null;
|
|
|
|
|
albumTitle = '';
|
|
|
|
|
showCategory = false;
|
|
|
|
|
category = '';
|
|
|
|
|
categoryLabels: Record<string, string> = {};
|
|
|
|
|
|
Ajout de la section galerie photo et nettoyage du template
Galerie photo :
- Ajout du layout photo avec slideshow plein écran
- Navigation par catégories (portraits, paysages, nature, etc.)
- Section "Fil Photo" avec posts illustrés (photoBlogPosts)
- Lightbox pour les albums de catégories
- Composants : Slideshow, CategoryNav, CategoryGrid, Lightbox, MasonryGallery
Nettoyage :
- Suppression du contenu démo du template (posts, images, about)
- Consolidation src/collections/ dans src/data/
- Suppression du config.js dupliqué (garde config.ts)
- Nettoyage des assets inutilisés (posts/, experiences/)
Corrections :
- Favicon récupéré du site actuel
- Chemins favicon corrigés dans les layouts
UI :
- Page d'accueil mise à jour
- Header/Footer simplifiés
- Nouvelle page À propos
2026-01-07 01:45:40 +01:00
|
|
|
constructor() {
|
|
|
|
|
this.lightbox = document.getElementById('lightbox');
|
2026-01-07 03:03:42 +01:00
|
|
|
this.lightboxImage = document.getElementById('lightbox-image') as HTMLImageElement | null;
|
Ajout de la section galerie photo et nettoyage du template
Galerie photo :
- Ajout du layout photo avec slideshow plein écran
- Navigation par catégories (portraits, paysages, nature, etc.)
- Section "Fil Photo" avec posts illustrés (photoBlogPosts)
- Lightbox pour les albums de catégories
- Composants : Slideshow, CategoryNav, CategoryGrid, Lightbox, MasonryGallery
Nettoyage :
- Suppression du contenu démo du template (posts, images, about)
- Consolidation src/collections/ dans src/data/
- Suppression du config.js dupliqué (garde config.ts)
- Nettoyage des assets inutilisés (posts/, experiences/)
Corrections :
- Favicon récupéré du site actuel
- Chemins favicon corrigés dans les layouts
UI :
- Page d'accueil mise à jour
- Header/Footer simplifiés
- Nouvelle page À propos
2026-01-07 01:45:40 +01:00
|
|
|
this.lightboxTitle = document.getElementById('lightbox-title');
|
|
|
|
|
this.lightboxCategory = document.getElementById('lightbox-category');
|
|
|
|
|
this.lightboxCounter = document.getElementById('lightbox-counter');
|
|
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init() {
|
|
|
|
|
if (window.lightboxData) {
|
|
|
|
|
this.images = window.lightboxData.images;
|
|
|
|
|
this.albumTitle = window.lightboxData.albumTitle;
|
|
|
|
|
this.showCategory = window.lightboxData.showCategory;
|
|
|
|
|
this.category = window.lightboxData.category;
|
|
|
|
|
this.categoryLabels = window.lightboxData.categoryLabels;
|
|
|
|
|
}
|
|
|
|
|
this.bindEvents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bindEvents() {
|
|
|
|
|
// Clic sur les éléments de galerie (thumbnails ou gallery-items)
|
|
|
|
|
document.querySelectorAll('.thumbnail-link, .gallery-item').forEach((item, index) => {
|
|
|
|
|
item.addEventListener('click', (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
this.openLightbox(index);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.querySelector('.lightbox-close')?.addEventListener('click', () => this.closeLightbox());
|
|
|
|
|
document.querySelector('.lightbox-fullscreen')?.addEventListener('click', () => this.toggleFullscreen());
|
|
|
|
|
document.querySelector('.lightbox-prev')?.addEventListener('click', () => this.prevImage());
|
|
|
|
|
document.querySelector('.lightbox-next')?.addEventListener('click', () => this.nextImage());
|
|
|
|
|
|
|
|
|
|
// Fermer en cliquant sur le fond
|
|
|
|
|
this.lightbox?.addEventListener('click', (e) => {
|
|
|
|
|
if (e.target === this.lightbox || (e.target as HTMLElement).classList.contains('lightbox-image-container')) {
|
|
|
|
|
this.closeLightbox();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
|
|
|
if (!this.lightbox?.classList.contains('hidden')) {
|
|
|
|
|
switch(e.key) {
|
|
|
|
|
case 'Escape': this.closeLightbox(); break;
|
|
|
|
|
case 'ArrowLeft': this.prevImage(); break;
|
|
|
|
|
case 'ArrowRight': this.nextImage(); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
openLightbox(index: number) {
|
|
|
|
|
this.currentIndex = index;
|
|
|
|
|
this.updateLightboxContent();
|
|
|
|
|
this.lightbox?.classList.remove('hidden');
|
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closeLightbox() {
|
|
|
|
|
this.lightbox?.classList.add('hidden');
|
|
|
|
|
document.body.style.overflow = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateLightboxContent() {
|
|
|
|
|
const image = this.images[this.currentIndex];
|
|
|
|
|
if (image && this.lightboxImage) {
|
|
|
|
|
this.lightboxImage.src = image.src;
|
|
|
|
|
this.lightboxImage.alt = image.alt;
|
|
|
|
|
|
|
|
|
|
// Titre: soit le titre de l'image, soit le titre de l'album
|
|
|
|
|
if (this.lightboxTitle) {
|
|
|
|
|
this.lightboxTitle.textContent = image.title || this.albumTitle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Catégorie (optionnel)
|
|
|
|
|
if (this.showCategory && this.lightboxCategory) {
|
|
|
|
|
const cat = image.category || this.category;
|
|
|
|
|
this.lightboxCategory.textContent = this.categoryLabels[cat] || cat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compteur
|
|
|
|
|
if (this.lightboxCounter) {
|
|
|
|
|
this.lightboxCounter.textContent = `${this.currentIndex + 1} / ${this.images.length}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextImage() {
|
|
|
|
|
this.currentIndex = (this.currentIndex + 1) % this.images.length;
|
|
|
|
|
this.updateLightboxContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prevImage() {
|
|
|
|
|
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
|
|
|
|
|
this.updateLightboxContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggleFullscreen() {
|
|
|
|
|
const fullscreenBtn = document.querySelector('.lightbox-fullscreen svg');
|
|
|
|
|
|
|
|
|
|
if (!document.fullscreenElement) {
|
|
|
|
|
this.lightbox?.requestFullscreen?.();
|
|
|
|
|
if (fullscreenBtn) {
|
|
|
|
|
fullscreenBtn.innerHTML = '<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
document.exitFullscreen?.();
|
|
|
|
|
if (fullscreenBtn) {
|
|
|
|
|
fullscreenBtn.innerHTML = '<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => new Lightbox());
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.lightbox {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: #000;
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox.hidden {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-image-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: calc(100% - 50px);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-image {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
max-height: 100%;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
display: block;
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-controls {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 20px;
|
|
|
|
|
right: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
z-index: 10001;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-fullscreen,
|
|
|
|
|
.lightbox-close {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: white;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transition: opacity 0.3s;
|
|
|
|
|
padding: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 30px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-fullscreen:hover,
|
|
|
|
|
.lightbox-close:hover {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-prev,
|
|
|
|
|
.lightbox-next {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
background: rgba(0, 0, 0, 0.3);
|
|
|
|
|
border: none;
|
|
|
|
|
color: white;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 30px 20px;
|
|
|
|
|
z-index: 10000;
|
|
|
|
|
transition: background 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-prev:hover,
|
|
|
|
|
.lightbox-next:hover {
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-prev {
|
|
|
|
|
left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-next {
|
|
|
|
|
right: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-prev svg,
|
|
|
|
|
.lightbox-next svg {
|
|
|
|
|
width: 30px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-bottom-bar {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
height: 50px;
|
|
|
|
|
background: rgba(0, 0, 0, 0.8);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lightbox-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-separator {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
</style>
|