Skip to content

Commit 0b04c7a

Browse files
feat: add responsive pagination to blogs page
1 parent 2ad1771 commit 0b04c7a

2 files changed

Lines changed: 185 additions & 1 deletion

File tree

src/pages/blogs/blogs-new.css

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2184,6 +2184,112 @@
21842184
left: 0;
21852185
}
21862186

2187+
.pagination-wrapper {
2188+
margin-top: 3rem;
2189+
}
2190+
2191+
.pagination-container {
2192+
display: grid;
2193+
grid-template-columns: 1fr auto 1fr;
2194+
align-items: center;
2195+
gap: 24px;
2196+
width: 100%;
2197+
}
2198+
2199+
.pagination-pages {
2200+
display: flex;
2201+
align-items: center;
2202+
justify-content: center;
2203+
gap: 8px;
2204+
}
2205+
2206+
.pagination-btn,
2207+
.pagination-number {
2208+
min-width: 34px;
2209+
height: 34px;
2210+
padding: 0 12px;
2211+
display: inline-flex;
2212+
align-items: center;
2213+
justify-content: center;
2214+
border-radius: 6px;
2215+
border: 1px solid #e5e7eb;
2216+
background: white;
2217+
color: #111827;
2218+
font-size: 13px;
2219+
font-weight: 600;
2220+
cursor: pointer;
2221+
transition: all 0.2s ease;
2222+
}
2223+
2224+
.pagination-btn {
2225+
min-width: 78px;
2226+
}
2227+
2228+
.pagination-btn:first-child {
2229+
justify-self: start;
2230+
}
2231+
2232+
.pagination-btn:last-child {
2233+
justify-self: end;
2234+
}
2235+
2236+
.pagination-btn:hover,
2237+
.pagination-number:hover {
2238+
border-color: #9ca3af;
2239+
color: #111827;
2240+
}
2241+
2242+
.active-page {
2243+
background: #475569;
2244+
color: white;
2245+
border-color: #475569;
2246+
}
2247+
2248+
.pagination-btn:disabled {
2249+
opacity: 0.5;
2250+
cursor: not-allowed;
2251+
transform: none;
2252+
}
2253+
2254+
.pagination-ellipsis {
2255+
color: #9ca3af;
2256+
font-size: 13px;
2257+
font-weight: 600;
2258+
line-height: 34px;
2259+
padding: 0 2px;
2260+
}
2261+
2262+
.pagination-summary {
2263+
color: #6b7280;
2264+
font-size: 12px;
2265+
margin: 14px 0 0;
2266+
text-align: center;
2267+
}
2268+
2269+
@media (max-width: 640px) {
2270+
.pagination-container {
2271+
grid-template-columns: 1fr;
2272+
gap: 8px;
2273+
}
2274+
2275+
.pagination-pages {
2276+
order: -1;
2277+
flex-wrap: wrap;
2278+
}
2279+
2280+
.pagination-btn,
2281+
.pagination-number {
2282+
min-width: 32px;
2283+
height: 32px;
2284+
font-size: 12px;
2285+
padding: 0 10px;
2286+
}
2287+
2288+
.pagination-btn {
2289+
width: 100%;
2290+
}
2291+
}
2292+
21872293
/* Responsive read more button */
21882294
@media (max-width: 767px) and (min-width: 576px) {
21892295
.card-read-more {

src/pages/blogs/index.tsx

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export default function Blogs() {
1313
const [searchInput, setSearchInput] = React.useState("");
1414
const [searchTerm, setSearchTerm] = React.useState("");
1515
const [filteredBlogs, setFilteredBlogs] = React.useState(blogs);
16+
const POSTS_PER_PAGE = 8;
17+
const [currentPage, setCurrentPage] = React.useState(1);
1618

1719
// Filter blogs after the user submits the blog search form.
1820
React.useEffect(() => {
@@ -31,8 +33,28 @@ export default function Blogs() {
3133
}
3234

3335
setFilteredBlogs(filtered);
36+
setCurrentPage(1);
3437
}, [searchTerm]);
3538

39+
React.useEffect(() => {
40+
window.scrollTo({
41+
top: 0,
42+
behavior: "smooth",
43+
});
44+
}, [currentPage]);
45+
46+
const totalPages = Math.ceil(filteredBlogs.length / POSTS_PER_PAGE);
47+
const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
48+
const endIndex = startIndex + POSTS_PER_PAGE;
49+
const currentBlogs = filteredBlogs.slice(startIndex, endIndex);
50+
const visiblePages = Array.from(
51+
{ length: Math.min(totalPages, 5) },
52+
(_, index) => index + 1,
53+
);
54+
const showLastPage = totalPages > 5;
55+
const showingStart = filteredBlogs.length === 0 ? 0 : startIndex + 1;
56+
const showingEnd = Math.min(endIndex, filteredBlogs.length);
57+
3658
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
3759
setSearchInput(e.target.value);
3860
};
@@ -156,7 +178,7 @@ export default function Blogs() {
156178

157179
<div className="articles-grid">
158180
{filteredBlogs.length > 0 ? (
159-
filteredBlogs.map((blog) => (
181+
currentBlogs.map((blog) => (
160182
<BlogCard key={blog.id ?? blog.slug} blog={blog} />
161183
))
162184
) : (
@@ -177,6 +199,62 @@ export default function Blogs() {
177199
</div>
178200
)}
179201
</div>
202+
203+
{filteredBlogs.length > POSTS_PER_PAGE && (
204+
<div className="pagination-wrapper">
205+
<div className="pagination-container">
206+
<button
207+
className="pagination-btn"
208+
disabled={currentPage === 1}
209+
aria-label="Go to previous page"
210+
onClick={() => setCurrentPage((prev) => prev - 1)}
211+
>
212+
Previous
213+
</button>
214+
215+
<div className="pagination-pages">
216+
{visiblePages.map((page) => (
217+
<button
218+
key={page}
219+
className={`pagination-number ${currentPage === page ? "active-page" : ""
220+
}`}
221+
aria-label={`Go to page ${page}`}
222+
onClick={() => setCurrentPage(page)}
223+
>
224+
{page}
225+
</button>
226+
))}
227+
228+
{showLastPage && (
229+
<>
230+
<span className="pagination-ellipsis">...</span>
231+
<button
232+
className={`pagination-number ${currentPage === totalPages ? "active-page" : ""
233+
}`}
234+
aria-label={`Go to page ${totalPages}`}
235+
onClick={() => setCurrentPage(totalPages)}
236+
>
237+
{totalPages}
238+
</button>
239+
</>
240+
)}
241+
</div>
242+
243+
<button
244+
className="pagination-btn"
245+
disabled={currentPage === totalPages}
246+
aria-label="Go to next page"
247+
onClick={() => setCurrentPage((prev) => prev + 1)}
248+
>
249+
Next
250+
</button>
251+
</div>
252+
<p className="pagination-summary">
253+
Showing {showingStart} - {showingEnd} of{" "}
254+
{filteredBlogs.length} posts
255+
</p>
256+
</div>
257+
)}
180258
</div>
181259
</div>
182260
</section>

0 commit comments

Comments
 (0)