Les images et les fichiers de contenu sont maintenant organisés par année (blog/2015/enigma/ au lieu de blog/enigma/) pour mieux s'y retrouver avec un volume croissant de posts. Le coverImage dans les frontmatters ne contient plus qu'un nom de fichier, résolu dynamiquement via import.meta.glob.
326 lines
No EOL
7.5 KiB
Text
326 lines
No EOL
7.5 KiB
Text
---
|
|
import PhotoLayout from '../../../layouts/PhotoLayout.astro';
|
|
import CategoryNav from '../../../components/photo/CategoryNav.astro';
|
|
import { getCollection } from 'astro:content';
|
|
import { Picture } from 'astro:assets';
|
|
|
|
// Importer toutes les images pour résoudre les cover images
|
|
const allImages = import.meta.glob<{ default: ImageMetadata }>(
|
|
'/src/assets/images/photos/blog/**/*.{jpg,jpeg,png,webp}'
|
|
);
|
|
|
|
// Récupération des posts photo (langue par défaut : FR)
|
|
const allPhotoBlogPosts = (await getCollection('photoBlogPosts'))
|
|
.filter(post => (post.data.lang ?? 'fr') === 'fr');
|
|
|
|
// Tri par date (plus récent en premier)
|
|
const sortedPosts = allPhotoBlogPosts.sort((a, b) =>
|
|
new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
|
|
);
|
|
|
|
// Résoudre les cover images via le glob
|
|
const postsWithImages = await Promise.all(sortedPosts.map(async (post) => {
|
|
const year = post.data.date.getFullYear();
|
|
const baseSlug = post.slug.replace(/^\d{4}\//, '').replace(/\.(en|ar)$/, '');
|
|
const coverPath = `/src/assets/images/photos/blog/${year}/${baseSlug}/${post.data.coverImage}`;
|
|
const loader = allImages[coverPath];
|
|
const resolvedCoverImage = loader ? (await loader()).default : undefined;
|
|
return { ...post, resolvedCoverImage };
|
|
}));
|
|
|
|
// Séparer les posts à la une des autres
|
|
const featuredPosts = postsWithImages.filter(post => post.data.featured);
|
|
const regularPosts = postsWithImages.filter(post => !post.data.featured);
|
|
---
|
|
|
|
<PhotoLayout title="Blog Photo - Jalil Arfaoui" enableScroll={true} hideFooter={false}>
|
|
<div class="blog-container">
|
|
<CategoryNav currentCategory="blog" opaque={false} />
|
|
|
|
<!-- Section héro avec posts à la une -->
|
|
{featuredPosts.length > 0 && (
|
|
<section class="featured-section">
|
|
<div class="featured-grid">
|
|
{featuredPosts.map(post => (
|
|
<article class="featured-post">
|
|
<a href={`/photo/blog/${post.slug}`} class="post-link">
|
|
<div class="post-image">
|
|
{post.resolvedCoverImage && <Picture src={post.resolvedCoverImage} alt={post.data.title} widths={[600, 900, 1200]} formats={['webp']} />}
|
|
<div class="post-overlay">
|
|
<div class="post-content">
|
|
<span class="post-badge">À la une</span>
|
|
<h2 class="post-title">{post.data.title}</h2>
|
|
<time class="post-date">
|
|
{new Date(post.data.date).toLocaleDateString('fr-FR', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
<!-- Grille des autres posts -->
|
|
<section class="posts-grid">
|
|
<div class="grid-container">
|
|
{regularPosts.map(post => (
|
|
<article class="post-item">
|
|
<a href={`/photo/blog/${post.slug}`} class="post-link">
|
|
{post.resolvedCoverImage && <Picture src={post.resolvedCoverImage} alt={post.data.title} widths={[400, 600, 800]} formats={['webp']} />}
|
|
<div class="post-overlay">
|
|
<div class="overlay-content">
|
|
<h3 class="post-title">{post.data.title}</h3>
|
|
<time class="post-date">
|
|
{new Date(post.data.date).toLocaleDateString('fr-FR', {
|
|
year: 'numeric',
|
|
month: 'short'
|
|
})}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</PhotoLayout>
|
|
|
|
<style>
|
|
.blog-container {
|
|
background: #000000;
|
|
color: #ffffff;
|
|
min-height: 100vh;
|
|
padding-top: 53px; /* Hauteur du header fixe */
|
|
}
|
|
|
|
/* Section à la une */
|
|
.featured-section {
|
|
padding: 16px 16px 0;
|
|
}
|
|
|
|
.featured-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.featured-post {
|
|
position: relative;
|
|
height: 450px;
|
|
overflow: hidden;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.featured-post .post-image {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
.featured-post img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.featured-post:hover img {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.featured-post .post-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.7) 100%);
|
|
display: flex;
|
|
align-items: flex-end;
|
|
padding: 40px;
|
|
}
|
|
|
|
.featured-post .post-content {
|
|
color: white;
|
|
}
|
|
|
|
.post-badge {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
padding: 4px 12px;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 12px;
|
|
display: inline-block;
|
|
}
|
|
|
|
.featured-post .post-title {
|
|
font-size: 28px;
|
|
font-weight: 600;
|
|
margin: 0 0 4px 0;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.featured-post .post-description {
|
|
font-size: 15px;
|
|
margin: 0 0 10px 0;
|
|
opacity: 0.9;
|
|
line-height: 1.3;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.featured-post .post-date {
|
|
display: block;
|
|
font-size: 14px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/* Grille des posts */
|
|
.posts-grid {
|
|
padding: 16px 16px 100px;
|
|
}
|
|
|
|
.grid-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 16px;
|
|
}
|
|
|
|
.post-item {
|
|
overflow: hidden;
|
|
border-radius: 4px;
|
|
background: #111;
|
|
}
|
|
|
|
.post-link {
|
|
display: block;
|
|
position: relative;
|
|
aspect-ratio: 3/2;
|
|
}
|
|
|
|
.featured-post .post-link {
|
|
aspect-ratio: auto;
|
|
height: 100%;
|
|
}
|
|
|
|
.post-link img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.post-item .post-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.5) 25%, rgba(0,0,0,0) 50%);
|
|
display: flex;
|
|
align-items: flex-end;
|
|
padding: 12px 16px;
|
|
transition: background 0.3s ease;
|
|
}
|
|
|
|
.post-item:hover .post-overlay {
|
|
background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.7) 25%, rgba(0,0,0,0) 50%);
|
|
}
|
|
|
|
.post-item:hover img {
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.post-item .overlay-content {
|
|
color: white;
|
|
}
|
|
|
|
.post-item .post-title {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
margin: 0 0 2px 0;
|
|
line-height: 1.3;
|
|
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
|
}
|
|
|
|
.post-item .post-subtitle {
|
|
font-size: 14px;
|
|
margin: 0 0 6px 0;
|
|
opacity: 0.9;
|
|
line-height: 1.2;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.post-item .post-date {
|
|
font-size: 12px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
|
|
/* Responsive */
|
|
@media (max-width: 1200px) {
|
|
.grid-container {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.featured-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: 12px;
|
|
}
|
|
|
|
.featured-post {
|
|
height: 300px;
|
|
}
|
|
|
|
.featured-post .post-overlay {
|
|
padding: 20px;
|
|
}
|
|
|
|
.featured-post .post-title {
|
|
font-size: 22px;
|
|
}
|
|
|
|
.grid-container {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.blog-container {
|
|
padding-top: 53px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.grid-container {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.featured-section {
|
|
padding: 12px 12px 0;
|
|
}
|
|
|
|
.posts-grid {
|
|
padding: 12px 12px 80px;
|
|
}
|
|
}
|
|
</style> |