jalil.arfaoui.net/src/components/photo/CategoryGrid.astro

307 lines
6.9 KiB
Text
Raw Normal View History

---
import CategoryNav from './CategoryNav.astro';
import ScrollIndicator from './ScrollIndicator.astro';
import Lightbox from './Lightbox.astro';
2026-01-07 03:03:42 +01:00
import { Picture } from 'astro:assets';
import { getEntry } from 'astro:content';
const { category } = Astro.props;
// Récupérer les métadonnées de la catégorie
const categoryData = await getEntry('photoCategories', category);
// Auto-détection des images du dossier de la catégorie
const allImages = import.meta.glob<{ default: ImageMetadata }>('/src/assets/images/photos/categories/**/*.{jpg,jpeg,png,webp}');
// Filtrer les images de cette catégorie
const categoryPath = `/src/assets/images/photos/categories/${category}/`;
const categoryImages = Object.entries(allImages)
.filter(([path]) => path.startsWith(categoryPath))
.sort(([a], [b]) => a.localeCompare(b));
// Résoudre les images et générer les métadonnées
const images = await Promise.all(
categoryImages.map(async ([path, loader]) => {
const img = await loader();
const filename = path.split('/').pop() || '';
const title = filename
.replace(/\.[^/.]+$/, '') // Enlever l'extension
.replace(/-/g, ' ') // Remplacer les tirets par des espaces
.replace(/_/g, ' '); // Remplacer les underscores par des espaces
return {
src: img.default,
alt: title,
title: title,
path: path
};
})
);
// Données pour la lightbox
const lightboxImages = images.map(img => ({
src: img.src.src,
alt: img.alt,
title: img.title
}));
---
<div id="category-gallery" class="category-container">
<CategoryNav currentCategory={category} opaque={true} />
<!-- Image hero avec titre en overlay -->
<header class="hero-cover">
<div class="hero-image">
2026-01-07 03:03:42 +01:00
{images[0] && <Picture src={images[0].src} alt={images[0].alt} widths={[800, 1200, 1920]} formats={['webp']} class="hero-bg" />}
<div class="hero-overlay">
<div class="hero-content">
<h1 class="hero-title">
<a href="#thumbnails" class="scroll-link">{categoryData?.data.title || category}</a>
</h1>
{categoryData?.data.subtitle && (
<p class="hero-subtitle">{categoryData.data.subtitle}</p>
)}
</div>
<ScrollIndicator targetSelector="#thumbnails" />
</div>
</div>
</header>
<!-- Grille de thumbnails -->
<div id="thumbnails" class="thumbnails-grid">
{images.map((image, index) => (
<div class="thumbnail-item">
<a href="#" class="thumbnail-link" data-image-index={index}>
2026-01-07 03:03:42 +01:00
<Picture
src={image.src}
alt={image.alt}
widths={[300, 500, 800]}
2026-01-07 03:03:42 +01:00
formats={['webp']}
class="thumbnail-image"
loading={index < 12 ? 'eager' : 'lazy'}
/>
<div class="thumbnail-overlay">
<div class="overlay-content">
<h3 class="thumbnail-title">{image.title}</h3>
</div>
</div>
</a>
</div>
))}
</div>
</div>
<Lightbox images={lightboxImages} showCategory={true} category={category} />
<script>
let lastScrollY = window.scrollY;
let ticking = false;
function updateNavVisibility() {
2026-01-07 03:03:42 +01:00
const header = document.querySelector('.category-navigation') as HTMLElement | null;
const footer = document.querySelector('footer') as HTMLElement | null;
const scrollY = window.scrollY;
if (scrollY > lastScrollY && scrollY > 100) {
header?.style.setProperty('transform', 'translateY(-100%)');
footer?.style.setProperty('transform', 'translateY(100%)');
} else {
header?.style.setProperty('transform', 'translateY(0)');
footer?.style.setProperty('transform', 'translateY(0)');
}
lastScrollY = scrollY;
}
function onScroll() {
if (!ticking) {
requestAnimationFrame(updateNavVisibility);
ticking = true;
requestAnimationFrame(() => { ticking = false; });
}
}
document.addEventListener('scroll', onScroll);
</script>
<style>
:root {
--header-height: 53px;
--footer-height: 54px;
--scroll-arrow-space: 200px;
--footer-clearance: 300px;
--grid-padding-top: 40px;
--grid-padding-horizontal: 20px;
--grid-column-gap: 20px;
--thumbnail-margin-bottom: 20px;
--grid-columns-desktop: 5;
--grid-columns-tablet: 3;
--grid-columns-mobile: 2;
--grid-columns-small: 1;
}
.category-container {
background: #000000;
color: #ffffff;
}
.hero-cover {
position: relative;
width: 100%;
height: calc(100vh - var(--header-height) - var(--footer-height));
overflow: hidden;
margin-top: var(--header-height);
}
.hero-image {
width: 100%;
height: 100%;
position: relative;
}
.hero-image :global(.hero-bg) {
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
top: 0;
left: 0;
}
.hero-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.41);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.hero-content {
text-align: center;
color: white;
}
.hero-title {
font-size: 64px;
font-weight: 600;
margin: 0 0 16px 0;
letter-spacing: -2px;
}
.hero-title .scroll-link {
color: white;
text-decoration: none;
border: none;
}
.hero-title .scroll-link:hover {
color: #a8a8a8;
}
.hero-subtitle {
font-size: 18px;
font-weight: 300;
margin: 0;
color: rgba(255, 255, 255, 0.9);
max-width: 600px;
line-height: 1.4;
}
.thumbnails-grid {
columns: var(--grid-columns-desktop);
column-gap: var(--grid-column-gap);
padding: var(--grid-padding-top) var(--grid-padding-horizontal) var(--footer-clearance);
margin: var(--scroll-arrow-space) auto 0;
max-width: 100%;
background: #000;
}
.thumbnail-item {
position: relative;
break-inside: avoid;
margin-bottom: var(--thumbnail-margin-bottom);
overflow: hidden;
background: #000;
}
.thumbnail-link {
display: block;
width: 100%;
border: none;
text-decoration: none;
}
.thumbnail-image {
width: 100%;
height: auto;
display: block;
transition: all 0.3s ease;
}
.thumbnail-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.overlay-content {
text-align: center;
padding: 15px;
}
.thumbnail-title {
color: white;
font-size: 14px;
font-weight: normal;
margin: 0;
line-height: 1.3;
}
.thumbnail-item:hover .thumbnail-overlay {
opacity: 1;
}
@media (max-width: 959px) {
.thumbnails-grid {
columns: var(--grid-columns-tablet);
}
}
@media (max-width: 767px) {
.thumbnails-grid {
columns: var(--grid-columns-mobile);
padding: var(--grid-padding-top) 10px var(--footer-clearance);
}
.thumbnail-overlay {
display: none;
}
.hero-title {
font-size: 36px;
letter-spacing: -1px;
}
.hero-subtitle {
font-size: 16px;
}
}
@media (max-width: 480px) {
.thumbnails-grid {
columns: var(--grid-columns-small);
}
}
</style>