fix: switch blog pagination to client-side
Astro 6 getStaticPaths+paginate has issues with non-dynamic pages. Client-side pagination works fine for small blog volumes.
This commit is contained in:
@@ -2,18 +2,14 @@
|
|||||||
import PageShell from '../components/PageShell.astro';
|
import PageShell from '../components/PageShell.astro';
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
|
|
||||||
export async function getStaticPaths({ paginate }) {
|
const POSTS_PER_PAGE = 12;
|
||||||
const posts = (await getCollection('blog', ({ data }) => !data.draft)).sort(
|
|
||||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
|
||||||
);
|
|
||||||
|
|
||||||
return paginate(posts, { pageSize: 12 });
|
const posts = (await getCollection('blog', ({ data }) => !data.draft)).sort(
|
||||||
}
|
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
|
);
|
||||||
const { page } = Astro.props;
|
|
||||||
|
|
||||||
// Collect all unique categories
|
// Collect all unique categories
|
||||||
const allCategories = [...new Set(page.data.map((p) => p.data.category))].sort();
|
const allCategories = [...new Set(posts.map((p) => p.data.category))].sort();
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageShell
|
<PageShell
|
||||||
@@ -38,7 +34,7 @@ const allCategories = [...new Set(page.data.map((p) => p.data.category))].sort()
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="blog-grid" id="blog-grid">
|
<div class="blog-grid" id="blog-grid">
|
||||||
{page.data.map((post) => {
|
{posts.map((post) => {
|
||||||
const fmt = new Intl.DateTimeFormat('th-TH', { day: '2-digit', month: 'short', year: '2-digit' }).format(post.data.pubDate);
|
const fmt = new Intl.DateTimeFormat('th-TH', { day: '2-digit', month: 'short', year: '2-digit' }).format(post.data.pubDate);
|
||||||
return (
|
return (
|
||||||
<a class="blog-card liquid-glass liquidGlass-wrapper" href={`/blog/${post.id}/`} data-cat={post.data.category}>
|
<a class="blog-card liquid-glass liquidGlass-wrapper" href={`/blog/${post.id}/`} data-cat={post.data.category}>
|
||||||
@@ -59,32 +55,83 @@ const allCategories = [...new Set(page.data.map((p) => p.data.category))].sort()
|
|||||||
<p class="blog-empty" id="blog-empty" hidden>ไม่พบบทความที่ตรงกับหมวดหมู่นี้</p>
|
<p class="blog-empty" id="blog-empty" hidden>ไม่พบบทความที่ตรงกับหมวดหมู่นี้</p>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
{page.lastPage > 1 && (
|
<nav class="blog-pagination" id="blog-pagination" aria-label="หน้าบทความ" hidden>
|
||||||
<nav class="blog-pagination" aria-label="หน้าบทความ">
|
<button class="pagination-btn" id="prev-btn" disabled>← ก่อนหน้า</button>
|
||||||
{page.url.prev ? (
|
<span class="pagination-info" id="page-info"></span>
|
||||||
<a class="pagination-btn" href={page.url.prev} aria-label="หน้าก่อนหน้า">
|
<button class="pagination-btn" id="next-btn">ถัดไป →</button>
|
||||||
← ก่อนหน้า
|
</nav>
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<span class="pagination-btn pagination-btn--disabled">← ก่อนหน้า</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span class="pagination-info">
|
|
||||||
หน้า {page.currentPage} จาก {page.lastPage}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{page.url.next ? (
|
|
||||||
<a class="pagination-btn" href={page.url.next} aria-label="หน้าถัดไป">
|
|
||||||
ถัดไป →
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<span class="pagination-btn pagination-btn--disabled">ถัดไป →</span>
|
|
||||||
)}
|
|
||||||
</nav>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
</PageShell>
|
</PageShell>
|
||||||
|
|
||||||
|
<script is:inline define:vars={{ POSTS_PER_PAGE }}>
|
||||||
|
const grid = document.getElementById('blog-grid');
|
||||||
|
const tagsEl = document.getElementById('blog-tags');
|
||||||
|
const empty = document.getElementById('blog-empty');
|
||||||
|
const pagination = document.getElementById('blog-pagination');
|
||||||
|
const prevBtn = document.getElementById('prev-btn');
|
||||||
|
const nextBtn = document.getElementById('next-btn');
|
||||||
|
const pageInfo = document.getElementById('page-info');
|
||||||
|
|
||||||
|
const allCards = Array.from(grid?.querySelectorAll('.blog-card') || []);
|
||||||
|
const tagBtns = tagsEl?.querySelectorAll('.blog-tag');
|
||||||
|
|
||||||
|
let currentPage = 1;
|
||||||
|
let activeCategory = 'all';
|
||||||
|
|
||||||
|
function getVisibleCards() {
|
||||||
|
return allCards.filter(card =>
|
||||||
|
activeCategory === 'all' || card.dataset.cat === activeCategory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDisplay() {
|
||||||
|
const visible = getVisibleCards();
|
||||||
|
const totalPages = Math.ceil(visible.length / POSTS_PER_PAGE);
|
||||||
|
const start = (currentPage - 1) * POSTS_PER_PAGE;
|
||||||
|
const end = start + POSTS_PER_PAGE;
|
||||||
|
|
||||||
|
// Hide all cards
|
||||||
|
allCards.forEach(card => card.style.display = 'none');
|
||||||
|
|
||||||
|
// Show current page cards
|
||||||
|
visible.slice(start, end).forEach(card => card.style.display = '');
|
||||||
|
|
||||||
|
// Update pagination
|
||||||
|
empty.hidden = visible.length > 0;
|
||||||
|
pagination.hidden = totalPages <= 1;
|
||||||
|
prevBtn.disabled = currentPage <= 1;
|
||||||
|
nextBtn.disabled = currentPage >= totalPages;
|
||||||
|
pageInfo.textContent = totalPages > 0
|
||||||
|
? `หน้า ${currentPage} จาก ${totalPages}`
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category filter
|
||||||
|
if (tagBtns) {
|
||||||
|
tagBtns.forEach((btn) => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
activeCategory = btn.dataset.cat;
|
||||||
|
currentPage = 1;
|
||||||
|
tagBtns.forEach((b) => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
updateDisplay();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination buttons
|
||||||
|
prevBtn?.addEventListener('click', () => {
|
||||||
|
if (currentPage > 1) { currentPage--; updateDisplay(); }
|
||||||
|
});
|
||||||
|
nextBtn?.addEventListener('click', () => {
|
||||||
|
const totalPages = Math.ceil(getVisibleCards().length / POSTS_PER_PAGE);
|
||||||
|
if (currentPage < totalPages) { currentPage++; updateDisplay(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial render
|
||||||
|
updateDisplay();
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.blog-section::before { display: none; }
|
.blog-section::before { display: none; }
|
||||||
|
|
||||||
@@ -194,17 +241,16 @@ const allCategories = [...new Set(page.data.map((p) => p.data.category))].sort()
|
|||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-decoration: none;
|
cursor: pointer;
|
||||||
transition: all .2s var(--ease);
|
transition: all .2s var(--ease);
|
||||||
}
|
}
|
||||||
.pagination-btn:hover {
|
.pagination-btn:hover:not(:disabled) {
|
||||||
background: var(--yellow);
|
background: var(--yellow);
|
||||||
border-color: var(--yellow);
|
border-color: var(--yellow);
|
||||||
}
|
}
|
||||||
.pagination-btn--disabled {
|
.pagination-btn:disabled {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
.pagination-info {
|
.pagination-info {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
@@ -233,31 +279,3 @@ const allCategories = [...new Set(page.data.map((p) => p.data.category))].sort()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
|
||||||
const tagsEl = document.getElementById('blog-tags');
|
|
||||||
const grid = document.getElementById('blog-grid');
|
|
||||||
const empty = document.getElementById('blog-empty');
|
|
||||||
const cards = grid?.querySelectorAll('.blog-card');
|
|
||||||
const tagBtns = tagsEl?.querySelectorAll('.blog-tag');
|
|
||||||
|
|
||||||
if (tagBtns && cards) {
|
|
||||||
tagBtns.forEach((btn) => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const cat = btn.dataset.cat;
|
|
||||||
tagBtns.forEach((b) => b.classList.remove('active'));
|
|
||||||
btn.classList.add('active');
|
|
||||||
let visible = 0;
|
|
||||||
cards.forEach((card) => {
|
|
||||||
if (cat === 'all' || card.dataset.cat === cat) {
|
|
||||||
card.style.display = '';
|
|
||||||
visible++;
|
|
||||||
} else {
|
|
||||||
card.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
empty.hidden = visible > 0;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user