mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 11:06:23 +00:00
feat: Refactor comment section to correctly handle API response structure
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s
fix: Update Gravatar URL size and improve avatar rendering logic style: Adjust footer margin for better layout consistency refactor: Remove old navbar component and integrate new layout structure feat: Enhance user profile page with user header component chore: Remove unused user profile component fix: Update posts per page configuration for better pagination feat: Extend device context to support system theme mode refactor: Remove unused device hook fix: Improve storage state hook for better error handling i18n: Add new translations for blog home page feat: Implement pagination component for better navigation feat: Create theme toggle component for improved user experience feat: Introduce responsive navbar or side layout with theme toggle feat: Develop custom select component for better UI consistency feat: Create user header component to display user information chore: Add query key constants for better code maintainability
This commit is contained in:
144
web/src/components/common/pagination.tsx
Normal file
144
web/src/components/common/pagination.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { useEffect, useState, useCallback } from "react"
|
||||
|
||||
export function PaginationController({
|
||||
initialPage = 1,
|
||||
totalPages = 10,
|
||||
buttons = 7, // recommended odd number >=5
|
||||
onPageChange,
|
||||
...props
|
||||
}: {
|
||||
initialPage?: number
|
||||
totalPages: number
|
||||
buttons?: number
|
||||
onPageChange?: (page: number) => void
|
||||
} & React.HTMLAttributes<HTMLDivElement>) {
|
||||
// normalize buttons
|
||||
const btns = Math.max(5, buttons ?? 7);
|
||||
const buttonsToShow = totalPages < btns ? totalPages : btns;
|
||||
// rely on shadcn buttonVariants and PaginationLink's isActive prop for styling
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(() => Math.min(Math.max(1, initialPage ?? 1), Math.max(1, totalPages)));
|
||||
const [direction, setDirection] = useState(0) // 1 = forward (right->left), -1 = backward
|
||||
|
||||
// sync when initialPage or totalPages props change
|
||||
useEffect(() => {
|
||||
const p = Math.min(Math.max(1, initialPage ?? 1), Math.max(1, totalPages));
|
||||
setCurrentPage(p);
|
||||
}, [initialPage, totalPages]);
|
||||
|
||||
const handleSetPage = useCallback((p: number) => {
|
||||
const next = Math.min(Math.max(1, Math.floor(p)), Math.max(1, totalPages));
|
||||
setDirection(next > currentPage ? 1 : next < currentPage ? -1 : 0);
|
||||
setCurrentPage(next);
|
||||
if (typeof onPageChange === 'function') onPageChange(next);
|
||||
}, [onPageChange, totalPages, currentPage]);
|
||||
|
||||
// helper to render page link
|
||||
const renderPage = (pageNum: number) => (
|
||||
<PaginationItem key={pageNum}>
|
||||
<PaginationLink
|
||||
isActive={pageNum === currentPage}
|
||||
aria-current={pageNum === currentPage ? 'page' : undefined}
|
||||
onClick={() => handleSetPage(pageNum)}
|
||||
type="button"
|
||||
>
|
||||
{pageNum}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
|
||||
// if totalPages small, render all
|
||||
if (totalPages <= buttonsToShow) {
|
||||
return (
|
||||
<Pagination>
|
||||
<PaginationContent className="select-none">
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
aria-disabled={currentPage === 1}
|
||||
onClick={() => currentPage > 1 && handleSetPage(currentPage - 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{Array.from({ length: totalPages }).map((_, i) => renderPage(i + 1))}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
aria-disabled={currentPage === totalPages}
|
||||
onClick={() => currentPage < totalPages && handleSetPage(currentPage + 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
);
|
||||
}
|
||||
|
||||
// for larger totalPages, show: 1, 2 or ellipsis, center range, ellipsis or N-1, N
|
||||
const centerCount = buttonsToShow - 4; // slots for center pages
|
||||
let start = currentPage - Math.floor(centerCount / 2);
|
||||
let end = start + centerCount - 1;
|
||||
if (start < 3) {
|
||||
start = 3;
|
||||
end = start + centerCount - 1;
|
||||
}
|
||||
if (end > totalPages - 2) {
|
||||
end = totalPages - 2;
|
||||
start = end - (centerCount - 1);
|
||||
}
|
||||
|
||||
const centerPages = [] as number[];
|
||||
for (let i = start; i <= end; i++) centerPages.push(i);
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<Pagination >
|
||||
<PaginationContent className="select-none">
|
||||
<PaginationItem>
|
||||
<PaginationPrevious aria-disabled={currentPage === 1} onClick={() => currentPage > 1 && handleSetPage(currentPage - 1)} />
|
||||
</PaginationItem>
|
||||
|
||||
{renderPage(1)}
|
||||
|
||||
{/* second slot: either page 2 or ellipsis if center starts later */}
|
||||
{start > 3 ? (
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
) : renderPage(2)}
|
||||
|
||||
{/* center pages */}
|
||||
{centerPages.map((p) => (
|
||||
<PaginationItem key={p}>
|
||||
<PaginationLink
|
||||
isActive={p === currentPage}
|
||||
aria-current={p === currentPage ? 'page' : undefined}
|
||||
onClick={() => handleSetPage(p)}
|
||||
type="button"
|
||||
>
|
||||
{p}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
|
||||
{end < totalPages - 2 ? (
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
) : renderPage(totalPages - 1)}
|
||||
|
||||
{renderPage(totalPages)}
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext aria-disabled={currentPage === totalPages} onClick={() => currentPage < totalPages && handleSetPage(currentPage + 1)} />
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user