Files
profile-front/src/components/ContentGrid.astro
Profile Profile f66c3baf8d add emmed
2026-03-14 18:01:30 +03:00

190 lines
5.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
import CategoryBadge from './CategoryBadge.astro';
export interface Props {
items: any[];
showCount?: boolean;
type: 'latest' | 'category' | 'author' | 'tag';
slug?: string;
pageInfo?: {
hasNextPage: boolean;
endCursor: string | null;
};
gridColumns?: 3 | 4;
}
const {
items = [],
showCount = false,
type = 'latest',
slug = '',
pageInfo = { hasNextPage: false, endCursor: null },
gridColumns = 4
} = Astro.props;
// Конфиг для клиентского скрипта
const loadMoreConfig = {
type,
slug,
gridColumns
};
function getCoauthorsNames(coauthors: any[]): string {
if (!coauthors || coauthors.length === 0) return '';
return coauthors
.map((coauthor: any) => {
const name = coauthor?.node?.name || coauthor?.name;
return name;
})
.filter(Boolean)
.join(', ');
}
function shouldBeLarge(index: number, columns: number): boolean {
if (columns === 4) {
// Паттерн для 4 колонок: большие на позициях 8, 19, 30, 41...
if (index < 8) return false;
return (index - 8) % 11 === 0;
} else {
// Паттерн для 3 колонок: большие на позициях 6, 14, 22, 30...
if (index < 6) return false;
return (index - 6) % 8 === 0;
}
}
---
<section class="posts-section" id="posts-section">
{showCount && items.length > 0 && (
<h2>Все статьи <span id="posts-count">({items.length})</span></h2>
)}
<div
id="posts-grid"
class="posts-grid"
data-grid-columns={gridColumns}
class:list={[`posts-grid-${gridColumns}`]}
>
{items.map((item, index) => {
const postUrl = item.uri || `/blog/${item.databaseId}`;
const postDate = new Date(item.date);
const isLarge = shouldBeLarge(index, gridColumns);
return (
<article
class={`post-card ${isLarge ? 'post-card-large' : ''}`}
data-large={isLarge}
data-index={index}
itemscope
itemtype="https://schema.org/BlogPosting"
>
<div class="post-image-container">
{item.featuredImage?.node?.sourceUrl ? (
<img
src={item.featuredImage.node.sourceUrl}
alt={item.featuredImage.node.altText || item.title}
width="400"
height="400"
loading="lazy"
class="post-image"
itemprop="image"
/>
) : (
<div class="post-image-placeholder"></div>
)}
{item.categories?.nodes?.[0] && (
<CategoryBadge
name={item.categories.nodes[0].name}
color={item.categories.nodes[0].color}
href={`/${item.categories.nodes[0].slug}`}
isNews={item.__typename === "ANew"}
/>
)}
<div class="post-content-overlay">
<div class="post-meta-overlay">
<time
datetime={item.date}
class="post-date-overlay"
itemprop="datePublished"
>
{postDate.toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'short',
year: 'numeric'
}).replace(' г.', '')}
</time>
</div>
<a href={postUrl} class="post-title-link" itemprop="url">
<h3 class="post-title-overlay" itemprop="headline">
{item.title}
</h3>
</a>
{item.coauthors && item.coauthors.length > 0 && (
<div class="coauthors-wrapper" itemprop="author">
{item.coauthors.map((coauthor: any, i: number) => {
const name = coauthor?.node?.name || coauthor?.name;
const nickname = coauthor?.node?.nickname || coauthor?.nickname;
return (
<span class="author-name" key={nickname || name}>
{i > 0 && ', '}
{nickname ? (
<a
href={`/author/${nickname}`}
class="author-link"
onClick={(e) => e.stopPropagation()}
>
{name}
</a>
) : (
<span class="author-name">{name}</span>
)}
</span>
);
})}
</div>
)}
</div>
</div>
<div class="sr-only">
<h3 itemprop="headline">
<a href={postUrl} itemprop="url">{item.title}</a>
</h3>
<time
datetime={item.date}
itemprop="datePublished"
>
{postDate.toLocaleDateString('ru-RU')}
</time>
</div>
</article>
);
})}
</div>
<div id="loading-indicator" class="loading-indicator" style="display: none;">
<div class="loading-spinner"></div>
<p>Загрузка...</p>
</div>
<div id="no-more-posts" class="no-more-posts" style="display: none;">
Все статьи загружены
</div>
{pageInfo.hasNextPage && (
<div
id="infinity-scroll-sentinel"
data-end-cursor={pageInfo.endCursor}
data-load-config={JSON.stringify(loadMoreConfig)}
data-current-index={items.length}
data-grid-columns={gridColumns}
></div>
)}
</section>