add correct infinity scroll
This commit is contained in:
@@ -2,14 +2,24 @@
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
items: any[];
|
items: any[];
|
||||||
showCount?: boolean;
|
showCount?: boolean;
|
||||||
|
pageInfo?: {
|
||||||
|
hasNextPage: boolean;
|
||||||
|
endCursor: string | null;
|
||||||
|
};
|
||||||
|
loadMoreConfig?: {
|
||||||
|
type: 'latest' | 'category' | 'author' | 'tag';
|
||||||
|
slug?: string;
|
||||||
|
first?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
items = [],
|
items = [],
|
||||||
showCount = false,
|
showCount = false,
|
||||||
|
pageInfo = { hasNextPage: false, endCursor: null },
|
||||||
|
loadMoreConfig = { type: 'latest', first: 11 }
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
// Функция для извлечения класса цвета из строки
|
|
||||||
function extractColorClass(colorString: string): string {
|
function extractColorClass(colorString: string): string {
|
||||||
if (!colorString) return 'bg-blue';
|
if (!colorString) return 'bg-blue';
|
||||||
|
|
||||||
@@ -44,7 +54,6 @@ function extractColorClass(colorString: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для получения списка имен соавторов
|
|
||||||
function getCoauthorsNames(coauthors: any[]): string {
|
function getCoauthorsNames(coauthors: any[]): string {
|
||||||
if (!coauthors || coauthors.length === 0) return '';
|
if (!coauthors || coauthors.length === 0) return '';
|
||||||
|
|
||||||
@@ -53,6 +62,14 @@ function getCoauthorsNames(coauthors: any[]): string {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ ИСПРАВЛЕННАЯ функция для определения больших карточек
|
||||||
|
// Большие карточки на индексах: 8, 19, 30, 41, 52...
|
||||||
|
// Формула: (index - 8) % 11 === 0
|
||||||
|
function shouldBeLarge(index: number): boolean {
|
||||||
|
if (index < 8) return false;
|
||||||
|
return (index - 8) % 11 === 0;
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="posts-section" id="posts-section">
|
<section class="posts-section" id="posts-section">
|
||||||
@@ -72,25 +89,14 @@ function getCoauthorsNames(coauthors: any[]): string {
|
|||||||
const rawColor = item.categories?.nodes?.[0]?.color || '';
|
const rawColor = item.categories?.nodes?.[0]?.color || '';
|
||||||
const categoryBgClass = extractColorClass(rawColor);
|
const categoryBgClass = extractColorClass(rawColor);
|
||||||
|
|
||||||
let isLarge = false;
|
// ✅ ИСПРАВЛЕННАЯ логика
|
||||||
let largePosition = '';
|
const isLarge = shouldBeLarge(index);
|
||||||
|
const largePosition = isLarge ? 'first' : '';
|
||||||
const rowNumber = Math.floor(index / 4) + 1;
|
|
||||||
const positionInRow = index % 4;
|
|
||||||
|
|
||||||
if (index >= 8) {
|
|
||||||
const largeRowStart = (rowNumber - 3) % 3 === 0 && rowNumber >= 3;
|
|
||||||
|
|
||||||
if (largeRowStart && positionInRow === 0) {
|
|
||||||
isLarge = true;
|
|
||||||
largePosition = 'first';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
class={`post-card ${isLarge ? 'post-card-large' : ''}`}
|
class={`post-card ${isLarge ? 'post-card-large' : ''}`}
|
||||||
data-large-position={isLarge ? largePosition : ''}
|
data-large-position={largePosition}
|
||||||
data-index={index}
|
data-index={index}
|
||||||
itemscope
|
itemscope
|
||||||
itemtype="https://schema.org/BlogPosting"
|
itemtype="https://schema.org/BlogPosting"
|
||||||
@@ -160,4 +166,359 @@ function getCoauthorsNames(coauthors: any[]): string {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
|
<!-- Sentinel для Intersection Observer -->
|
||||||
|
{pageInfo.hasNextPage && (
|
||||||
|
<div
|
||||||
|
id="infinity-scroll-sentinel"
|
||||||
|
data-end-cursor={pageInfo.endCursor}
|
||||||
|
data-load-config={JSON.stringify(loadMoreConfig)}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
interface PageInfo {
|
||||||
|
hasNextPage: boolean;
|
||||||
|
endCursor: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoadMoreConfig {
|
||||||
|
type: 'latest' | 'category' | 'author' | 'tag';
|
||||||
|
slug?: string;
|
||||||
|
first?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Post {
|
||||||
|
id: string;
|
||||||
|
databaseId: number;
|
||||||
|
title: string;
|
||||||
|
uri: string;
|
||||||
|
date: string;
|
||||||
|
featuredImage?: {
|
||||||
|
node?: {
|
||||||
|
sourceUrl: string;
|
||||||
|
altText: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
coauthors?: Array<{
|
||||||
|
name?: string;
|
||||||
|
node?: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
categories?: {
|
||||||
|
nodes?: Array<{
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoadPostsResponse {
|
||||||
|
posts: Post[];
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class InfinityScroll {
|
||||||
|
private grid: HTMLElement | null;
|
||||||
|
private sentinel: HTMLElement | null;
|
||||||
|
private loadingIndicator: HTMLElement | null;
|
||||||
|
private noMorePosts: HTMLElement | null;
|
||||||
|
private postsCount: HTMLElement | null;
|
||||||
|
private observer: IntersectionObserver | null = null;
|
||||||
|
private isLoading = false;
|
||||||
|
private hasMore = true;
|
||||||
|
private endCursor: string | null = null;
|
||||||
|
private currentIndex = 0;
|
||||||
|
private loadMoreConfig: LoadMoreConfig;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.grid = document.getElementById('posts-grid');
|
||||||
|
this.sentinel = document.getElementById('infinity-scroll-sentinel');
|
||||||
|
this.loadingIndicator = document.getElementById('loading-indicator');
|
||||||
|
this.noMorePosts = document.getElementById('no-more-posts');
|
||||||
|
this.postsCount = document.getElementById('posts-count');
|
||||||
|
|
||||||
|
const defaultConfig: LoadMoreConfig = { type: 'latest', first: 11 };
|
||||||
|
|
||||||
|
if (this.sentinel) {
|
||||||
|
this.endCursor = this.sentinel.dataset.endCursor || null;
|
||||||
|
this.currentIndex = this.grid?.children.length || 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loadMoreConfig = JSON.parse(this.sentinel.dataset.loadConfig || '{}');
|
||||||
|
this.loadMoreConfig = { ...defaultConfig, ...this.loadMoreConfig };
|
||||||
|
} catch {
|
||||||
|
this.loadMoreConfig = defaultConfig;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.loadMoreConfig = defaultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
if (!this.sentinel || !this.grid) return;
|
||||||
|
|
||||||
|
this.observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.isIntersecting && !this.isLoading && this.hasMore) {
|
||||||
|
this.loadMorePosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rootMargin: '200px',
|
||||||
|
threshold: 0.1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.observer.observe(this.sentinel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadMorePosts() {
|
||||||
|
if (this.isLoading || !this.hasMore) return;
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
this.showLoading();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/posts', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
first: this.loadMoreConfig.first || 11,
|
||||||
|
after: this.endCursor,
|
||||||
|
type: this.loadMoreConfig.type,
|
||||||
|
slug: this.loadMoreConfig.slug
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка загрузки постов');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: LoadPostsResponse = await response.json();
|
||||||
|
|
||||||
|
if (data.posts && data.posts.length > 0) {
|
||||||
|
this.appendPosts(data.posts);
|
||||||
|
this.endCursor = data.pageInfo.endCursor;
|
||||||
|
this.hasMore = data.pageInfo.hasNextPage;
|
||||||
|
|
||||||
|
if (!this.hasMore) {
|
||||||
|
this.showNoMorePosts();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.hasMore = false;
|
||||||
|
this.showNoMorePosts();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка загрузки:', error);
|
||||||
|
this.hasMore = false;
|
||||||
|
this.showError();
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private appendPosts(posts: Post[]) {
|
||||||
|
if (!this.grid) return;
|
||||||
|
|
||||||
|
posts.forEach((post) => {
|
||||||
|
const article = this.createPostCard(post, this.currentIndex);
|
||||||
|
this.grid?.appendChild(article);
|
||||||
|
this.currentIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.postsCount) {
|
||||||
|
this.postsCount.textContent = ` (${this.currentIndex})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPostCard(post: Post, index: number): HTMLElement {
|
||||||
|
const article = document.createElement('article');
|
||||||
|
article.className = 'post-card';
|
||||||
|
article.setAttribute('itemscope', '');
|
||||||
|
article.setAttribute('itemtype', 'https://schema.org/BlogPosting');
|
||||||
|
article.dataset.index = String(index);
|
||||||
|
|
||||||
|
// ✅ ИСПРАВЛЕННАЯ логика для больших карточек
|
||||||
|
// Большие карточки на индексах: 8, 19, 30, 41, 52, 63...
|
||||||
|
// Формула: (index - 8) % 11 === 0
|
||||||
|
const isLarge = index >= 8 && (index - 8) % 11 === 0;
|
||||||
|
|
||||||
|
if (isLarge) {
|
||||||
|
article.classList.add('post-card-large');
|
||||||
|
article.dataset.largePosition = 'first';
|
||||||
|
console.log(`[Large card] Index: ${index}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const postUrl = post.uri || `/blog/${post.databaseId}`;
|
||||||
|
const postDate = new Date(post.date);
|
||||||
|
const coauthorsNames = this.getCoauthorsNames(post.coauthors || []);
|
||||||
|
|
||||||
|
const categoryName = post.categories?.nodes?.[0]?.name || '';
|
||||||
|
const categoryColor = post.categories?.nodes?.[0]?.color || '';
|
||||||
|
const categoryClass = this.extractColorClass(categoryColor);
|
||||||
|
|
||||||
|
const imageUrl = post.featuredImage?.node?.sourceUrl;
|
||||||
|
const imageAlt = post.featuredImage?.node?.altText || post.title;
|
||||||
|
|
||||||
|
article.innerHTML = `
|
||||||
|
<a href="${postUrl}" class="post-card-link">
|
||||||
|
<div class="post-image-container">
|
||||||
|
${imageUrl
|
||||||
|
? `<img src="${imageUrl}" alt="${imageAlt}" width="400" height="400" loading="lazy" class="post-image" itemprop="image" />`
|
||||||
|
: '<div class="post-image-placeholder"></div>'
|
||||||
|
}
|
||||||
|
|
||||||
|
${categoryName
|
||||||
|
? `<div class="post-category-badge ${categoryClass}">${categoryName}</div>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="post-content-overlay">
|
||||||
|
<div class="post-meta-overlay">
|
||||||
|
<time datetime="${post.date}" class="post-date-overlay" itemprop="datePublished">
|
||||||
|
${postDate.toLocaleDateString('ru-RU', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
year: 'numeric'
|
||||||
|
}).replace(' г.', '')}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="post-title-overlay" itemprop="headline">
|
||||||
|
${post.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
${coauthorsNames
|
||||||
|
? `<div class="author-name" itemprop="author">${coauthorsNames}</div>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="sr-only">
|
||||||
|
<h3 itemprop="headline">
|
||||||
|
<a href="${postUrl}" itemprop="url">${post.title}</a>
|
||||||
|
</h3>
|
||||||
|
<time datetime="${post.date}" itemprop="datePublished">
|
||||||
|
${postDate.toLocaleDateString('ru-RU')}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCoauthorsNames(coauthors: any[]): string {
|
||||||
|
if (!coauthors || coauthors.length === 0) return '';
|
||||||
|
return coauthors
|
||||||
|
.map(c => c?.node?.name || c?.name)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractColorClass(colorString: string): string {
|
||||||
|
if (!colorString) return 'bg-blue';
|
||||||
|
|
||||||
|
if (colorString.includes('фон меню:')) {
|
||||||
|
const parts = colorString.split(':');
|
||||||
|
const color = parts[1]?.trim();
|
||||||
|
|
||||||
|
const validColors = [
|
||||||
|
'black', 'yellow', 'blue', 'green', 'red', 'orange', 'gray',
|
||||||
|
'indigo', 'purple', 'pink', 'teal', 'cyan', 'white',
|
||||||
|
'gray-dark', 'light', 'dark'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (color && validColors.includes(color)) {
|
||||||
|
return `bg-${color}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorString.startsWith('bg-')) {
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
const simpleColor = colorString.toLowerCase();
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
'black': 'bg-black', 'yellow': 'bg-yellow', 'blue': 'bg-blue',
|
||||||
|
'green': 'bg-green', 'red': 'bg-red', 'orange': 'bg-orange',
|
||||||
|
'gray': 'bg-gray', 'indigo': 'bg-indigo', 'purple': 'bg-purple',
|
||||||
|
'pink': 'bg-pink', 'teal': 'bg-teal', 'cyan': 'bg-cyan',
|
||||||
|
'white': 'bg-white', 'dark': 'bg-dark', 'light': 'bg-light',
|
||||||
|
'gray-dark': 'bg-gray-dark'
|
||||||
|
};
|
||||||
|
|
||||||
|
return colorMap[simpleColor] || 'bg-blue';
|
||||||
|
}
|
||||||
|
|
||||||
|
private showLoading() {
|
||||||
|
if (this.loadingIndicator) {
|
||||||
|
this.loadingIndicator.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hideLoading() {
|
||||||
|
if (this.loadingIndicator) {
|
||||||
|
this.loadingIndicator.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private showNoMorePosts() {
|
||||||
|
if (this.sentinel && this.observer) {
|
||||||
|
this.observer.unobserve(this.sentinel);
|
||||||
|
this.sentinel.style.display = 'none';
|
||||||
|
this.sentinel.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.noMorePosts) {
|
||||||
|
this.noMorePosts.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private showError() {
|
||||||
|
if (this.noMorePosts) {
|
||||||
|
this.noMorePosts.textContent = 'Ошибка загрузки. Попробуйте обновить страницу.';
|
||||||
|
this.noMorePosts.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
if (this.observer && this.sentinel) {
|
||||||
|
this.observer.unobserve(this.sentinel);
|
||||||
|
}
|
||||||
|
this.observer?.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let infinityScroll: InfinityScroll | null = null;
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
infinityScroll = new InfinityScroll();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('astro:before-swap', () => {
|
||||||
|
infinityScroll?.destroy();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
78
src/pages/api/posts.ts
Normal file
78
src/pages/api/posts.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
|
||||||
|
// ✅ Добавьте эту строку!
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
export const POST: APIRoute = async ({ request }) => {
|
||||||
|
console.log('[API] Request received');
|
||||||
|
console.log('[API] Content-Type:', request.headers.get('content-type'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ✅ Используйте request.text() вместо request.json()
|
||||||
|
const rawBody = await request.text();
|
||||||
|
console.log('[API] Raw body length:', rawBody?.length || 0);
|
||||||
|
console.log('[API] Raw body:', rawBody);
|
||||||
|
|
||||||
|
if (!rawBody || rawBody.trim() === '') {
|
||||||
|
throw new Error('Request body is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = JSON.parse(rawBody);
|
||||||
|
console.log('[API] Parsed body:', body);
|
||||||
|
|
||||||
|
const {
|
||||||
|
first = 14,
|
||||||
|
after = null,
|
||||||
|
type = 'latest',
|
||||||
|
slug = null
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
console.log('[API] Parsed params:', { first, after, type, slug });
|
||||||
|
|
||||||
|
// ✅ Используйте относительный путь, НЕ алиас @api
|
||||||
|
const { getLatestPosts } = await import('../../lib/api/posts.js');
|
||||||
|
|
||||||
|
console.log('[API] Calling getLatestPosts with:', { first, after });
|
||||||
|
|
||||||
|
const data = await getLatestPosts(first, after);
|
||||||
|
|
||||||
|
console.log('[API] Data received:', {
|
||||||
|
postsCount: data?.posts?.length || 0,
|
||||||
|
hasNextPage: data?.pageInfo?.hasNextPage,
|
||||||
|
endCursor: data?.pageInfo?.endCursor
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data || !data.posts) {
|
||||||
|
throw new Error('Invalid data structure returned from getLatestPosts');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(data), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[API] Error details:', {
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
name: error.name
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: error.message || 'Ошибка загрузки постов',
|
||||||
|
details: process.env.NODE_ENV === 'development' ? error.stack : undefined,
|
||||||
|
posts: [],
|
||||||
|
pageInfo: { hasNextPage: false, endCursor: null }
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -3,17 +3,15 @@ import { getSiteInfo } from "../lib/wp-api.js";
|
|||||||
import { getLatestPosts } from '@api/posts.js';
|
import { getLatestPosts } from '@api/posts.js';
|
||||||
|
|
||||||
const site = await getSiteInfo();
|
const site = await getSiteInfo();
|
||||||
const initialPosts = await getLatestPosts(37); // Начальная загрузка 12 постов
|
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
|
||||||
|
|
||||||
// визуальные компоненты
|
// визуальные компоненты
|
||||||
import MainLayout from '@layouts/MainLayout.astro';
|
import MainLayout from '@layouts/MainLayout.astro';
|
||||||
import ContentGrid from '@components/ContentGrid.astro';
|
import ContentGrid from '@components/ContentGrid.astro';
|
||||||
import EndnewsList from '@components/EndnewsList.astro';
|
import EndnewsList from '@components/EndnewsList.astro';
|
||||||
|
|
||||||
|
|
||||||
//ISR
|
//ISR
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
//export const revalidate = 1;
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout
|
<MainLayout
|
||||||
@@ -27,8 +25,11 @@ export const prerender = false;
|
|||||||
<EndnewsList />
|
<EndnewsList />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ContentGrid
|
||||||
<ContentGrid items={initialPosts.posts} />
|
items={posts}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
showCount={true}
|
||||||
|
loadMoreConfig={{ type: 'latest', first: 11 }}
|
||||||
|
/>
|
||||||
|
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
@@ -167,6 +167,34 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Убедитесь что индикаторы не занимают лишнее место */
|
||||||
|
#infinity-scroll-sentinel {
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-indicator {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #666;
|
||||||
|
min-height: auto; /* ← Важно! */
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more-posts {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
margin-top: 20px;
|
||||||
|
min-height: auto; /* ← Важно! */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Улучшения для больших карточек */
|
/* Улучшения для больших карточек */
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
.post-card-large .post-category-badge {
|
.post-card-large .post-category-badge {
|
||||||
|
|||||||
Reference in New Issue
Block a user