-
Notifications
You must be signed in to change notification settings - Fork 410
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from usual2970/feat/pagination
Feat/pagination
- Loading branch information
Showing
12 changed files
with
553 additions
and
263 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { | ||
Pagination, | ||
PaginationContent, | ||
PaginationEllipsis, | ||
PaginationItem, | ||
PaginationLink, | ||
} from "../ui/pagination"; | ||
|
||
type PaginationProps = { | ||
totalPages: number; | ||
currentPage: number; | ||
onPageChange: (page: number) => void; | ||
}; | ||
|
||
type PageNumber = number | string; | ||
|
||
const XPagination = ({ | ||
totalPages, | ||
currentPage, | ||
onPageChange, | ||
}: PaginationProps) => { | ||
const pageNeighbours = 1; // Number of page numbers to show on either side of the current page | ||
|
||
const getPageNumbers = () => { | ||
const totalNumbers = pageNeighbours * 2 + 3; // total pages to display (left + right neighbours + current + 2 for start and end) | ||
const totalBlocks = totalNumbers + 2; // adding 2 for the start and end page numbers | ||
|
||
if (totalPages > totalBlocks) { | ||
let pages: PageNumber[] = []; | ||
|
||
const leftBound = Math.max(2, currentPage - pageNeighbours); | ||
const rightBound = Math.min(totalPages - 1, currentPage + pageNeighbours); | ||
|
||
const beforeLastPage = totalPages - 1; | ||
|
||
pages = range(leftBound, rightBound); | ||
|
||
if (currentPage > pageNeighbours + 2) { | ||
pages.unshift("..."); | ||
} | ||
if (currentPage < beforeLastPage - pageNeighbours) { | ||
pages.push("..."); | ||
} | ||
|
||
pages.unshift(1); | ||
pages.push(totalPages); | ||
|
||
return pages; | ||
} | ||
|
||
return range(1, totalPages); | ||
}; | ||
|
||
const range = (from: number, to: number, step = 1) => { | ||
let i = from; | ||
const range = []; | ||
|
||
while (i <= to) { | ||
range.push(i); | ||
i += step; | ||
} | ||
|
||
return range; | ||
}; | ||
|
||
const pages = getPageNumbers(); | ||
|
||
return ( | ||
<> | ||
<Pagination className="dark:text-stone-200 justify-end mt-3"> | ||
<PaginationContent> | ||
{pages.map((page, index) => { | ||
if (page === "...") { | ||
return ( | ||
<PaginationItem key={index}> | ||
<PaginationEllipsis /> | ||
</PaginationItem> | ||
); | ||
} | ||
|
||
return ( | ||
<PaginationItem key={index}> | ||
<PaginationLink | ||
href="#" | ||
isActive={currentPage == page} | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
onPageChange(page as number); | ||
}} | ||
> | ||
{page} | ||
</PaginationLink> | ||
</PaginationItem> | ||
); | ||
})} | ||
</PaginationContent> | ||
</Pagination> | ||
</> | ||
); | ||
}; | ||
|
||
export default XPagination; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import * as React from "react"; | ||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
import { ButtonProps, buttonVariants } from "@/components/ui/button"; | ||
|
||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( | ||
<nav | ||
role="navigation" | ||
aria-label="pagination" | ||
className={cn("mx-auto flex w-full justify-center", className)} | ||
{...props} | ||
/> | ||
); | ||
Pagination.displayName = "Pagination"; | ||
|
||
const PaginationContent = React.forwardRef< | ||
HTMLUListElement, | ||
React.ComponentProps<"ul"> | ||
>(({ className, ...props }, ref) => ( | ||
<ul | ||
ref={ref} | ||
className={cn("flex flex-row items-center gap-1", className)} | ||
{...props} | ||
/> | ||
)); | ||
PaginationContent.displayName = "PaginationContent"; | ||
|
||
const PaginationItem = React.forwardRef< | ||
HTMLLIElement, | ||
React.ComponentProps<"li"> | ||
>(({ className, ...props }, ref) => ( | ||
<li ref={ref} className={cn("", className)} {...props} /> | ||
)); | ||
PaginationItem.displayName = "PaginationItem"; | ||
|
||
type PaginationLinkProps = { | ||
isActive?: boolean; | ||
} & Pick<ButtonProps, "size"> & | ||
React.ComponentProps<"a">; | ||
|
||
const PaginationLink = ({ | ||
className, | ||
isActive, | ||
size = "icon", | ||
...props | ||
}: PaginationLinkProps) => ( | ||
<a | ||
aria-current={isActive ? "page" : undefined} | ||
className={cn( | ||
buttonVariants({ | ||
variant: isActive ? "outline" : "ghost", | ||
size, | ||
}), | ||
className | ||
)} | ||
{...props} | ||
/> | ||
); | ||
PaginationLink.displayName = "PaginationLink"; | ||
|
||
const PaginationPrevious = ({ | ||
className, | ||
...props | ||
}: React.ComponentProps<typeof PaginationLink>) => ( | ||
<PaginationLink | ||
aria-label="Go to previous page" | ||
size="default" | ||
className={cn("gap-1 pl-2.5", className)} | ||
{...props} | ||
> | ||
<ChevronLeft className="h-4 w-4" /> | ||
<span>上一页</span> | ||
</PaginationLink> | ||
); | ||
PaginationPrevious.displayName = "PaginationPrevious"; | ||
|
||
const PaginationNext = ({ | ||
className, | ||
...props | ||
}: React.ComponentProps<typeof PaginationLink>) => ( | ||
<PaginationLink | ||
aria-label="Go to next page" | ||
size="default" | ||
className={cn("gap-1 pr-2.5", className)} | ||
{...props} | ||
> | ||
<span>下一页</span> | ||
<ChevronRight className="h-4 w-4" /> | ||
</PaginationLink> | ||
); | ||
PaginationNext.displayName = "PaginationNext"; | ||
|
||
const PaginationEllipsis = ({ | ||
className, | ||
...props | ||
}: React.ComponentProps<"span">) => ( | ||
<span | ||
aria-hidden | ||
className={cn("flex h-9 w-9 items-center justify-center", className)} | ||
{...props} | ||
> | ||
<MoreHorizontal className="h-4 w-4" /> | ||
<span className="sr-only">More pages</span> | ||
</span> | ||
); | ||
PaginationEllipsis.displayName = "PaginationEllipsis"; | ||
|
||
export { | ||
Pagination, | ||
PaginationContent, | ||
PaginationEllipsis, | ||
PaginationItem, | ||
PaginationLink, | ||
PaginationNext, | ||
PaginationPrevious, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.