-
-
Notifications
You must be signed in to change notification settings - Fork 750
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cf3ca64
commit af0e6a1
Showing
5 changed files
with
223 additions
and
2 deletions.
There are no files selected for viewing
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,30 @@ | ||
import { useMemo, useState } from 'react'; | ||
|
||
/** | ||
* @description Custom hook for managing pagination logic | ||
* @example const { currentPage, setCurrentPage, currentItems, maxPage } = usePagination(items, 10); | ||
* @param {T[]} items - Array of items to paginate | ||
* @param {number} itemsPerPage - Number of items per page | ||
* @returns {object} | ||
* @returns {number} currentPage - Current page number | ||
* @returns {function} setCurrentPage - Function to update the current page | ||
* @returns {T[]} currentItems - Items for the current page | ||
* @returns {number} maxPage - Total number of pages | ||
*/ | ||
export function usePagination<T>(items: T[], itemsPerPage: number) { | ||
const [currentPage, setCurrentPage] = useState(1); | ||
const maxPage = Math.ceil(items.length / itemsPerPage); | ||
|
||
const currentItems = useMemo(() => { | ||
const start = (currentPage - 1) * itemsPerPage; | ||
|
||
return items.slice(start, start + itemsPerPage); | ||
}, [items, currentPage, itemsPerPage]); | ||
|
||
return { | ||
currentPage, | ||
setCurrentPage, | ||
currentItems, | ||
maxPage | ||
}; | ||
} |
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,122 @@ | ||
import React from 'react'; | ||
|
||
import PaginationItem from './PaginationItem'; | ||
|
||
export interface PaginationProps { | ||
// eslint-disable-next-line prettier/prettier | ||
|
||
/** Total number of pages */ | ||
totalPages: number; | ||
|
||
/** Current active page */ | ||
currentPage: number; | ||
|
||
/** Function to handle page changes */ | ||
onPageChange: (page: number) => void; | ||
} | ||
|
||
/** | ||
* This is the Pagination component. It displays a list of page numbers that can be clicked to navigate. | ||
*/ | ||
export default function Pagination({ totalPages, currentPage, onPageChange }: PaginationProps) { | ||
const handlePageChange = (page: number) => { | ||
if (page < 1 || page > totalPages) return; | ||
onPageChange(page); | ||
}; | ||
|
||
const getPageNumbers = () => { | ||
const pages = []; | ||
|
||
if (totalPages <= 7) { | ||
for (let i = 1; i <= totalPages; i++) pages.push(i); | ||
} else { | ||
pages.push(1); | ||
if (currentPage > 3) { | ||
pages.push('ellipsis1'); | ||
} | ||
|
||
for (let i = Math.max(2, currentPage - 1); i <= Math.min(totalPages - 1, currentPage + 1); i++) { | ||
pages.push(i); | ||
} | ||
|
||
if (currentPage < totalPages - 2) { | ||
pages.push('ellipsis2'); | ||
} | ||
|
||
pages.push(totalPages); | ||
} | ||
|
||
return pages; | ||
}; | ||
|
||
return ( | ||
<div className='font-inter flex items-center justify-center gap-8'> | ||
{/* Previous button */} | ||
<button | ||
onClick={() => handlePageChange(currentPage - 1)} | ||
disabled={currentPage === 1} | ||
className={` | ||
font-normal flex h-[34px] items-center justify-center gap-2 rounded bg-white px-4 | ||
py-[7px] text-sm leading-[17px] tracking-[-0.01em] | ||
${currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'} | ||
`} | ||
> | ||
<svg | ||
width='20' | ||
height='20' | ||
viewBox='0 0 24 24' | ||
fill='none' | ||
xmlns='http://www.w3.org/2000/svg' | ||
className='stroke-current' | ||
> | ||
<path d='M15 18L9 12L15 6' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' /> | ||
</svg> | ||
<span>Previous</span> | ||
</button> | ||
|
||
{/* Page numbers */} | ||
<div className='flex gap-2'> | ||
{getPageNumbers().map((page) => | ||
typeof page === 'number' ? ( | ||
<PaginationItem | ||
key={page} | ||
pageNumber={page} | ||
isActive={page === currentPage} | ||
onPageChange={handlePageChange} | ||
/> | ||
) : ( | ||
<span | ||
key={page} | ||
className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]' | ||
> | ||
... | ||
</span> | ||
) | ||
)} | ||
</div> | ||
|
||
{/* Next button */} | ||
<button | ||
onClick={() => handlePageChange(currentPage + 1)} | ||
disabled={currentPage === totalPages} | ||
className={` | ||
font-normal flex h-[34px] items-center justify-center gap-2 rounded bg-white px-4 | ||
py-[7px] text-sm leading-[17px] tracking-[-0.01em] | ||
${currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'} | ||
`} | ||
> | ||
<span>Next</span> | ||
<svg | ||
width='20' | ||
height='20' | ||
viewBox='0 0 24 24' | ||
fill='none' | ||
xmlns='http://www.w3.org/2000/svg' | ||
className='stroke-current' | ||
> | ||
<path d='M9 6L15 12L9 18' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' /> | ||
</svg> | ||
</button> | ||
</div> | ||
); | ||
} |
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,32 @@ | ||
import React from 'react'; | ||
|
||
export interface PaginationItemProps { | ||
// eslint-disable-next-line prettier/prettier | ||
|
||
/** The page number to display */ | ||
pageNumber: number; | ||
|
||
/** Whether this page is currently active */ | ||
isActive: boolean; | ||
|
||
/** Function to handle page change */ | ||
onPageChange: (page: number) => void; | ||
} | ||
|
||
/** | ||
* This is the PaginationItem component. It displays a single page number that can be clicked. | ||
*/ | ||
export default function PaginationItem({ pageNumber, isActive, onPageChange }: PaginationItemProps) { | ||
return ( | ||
<button | ||
onClick={() => onPageChange(pageNumber)} | ||
className={`font-inter font-normal relative flex size-10 items-center | ||
justify-center rounded-full text-sm leading-[26px] | ||
${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'} | ||
`} | ||
aria-current={isActive ? 'page' : undefined} | ||
> | ||
{pageNumber} | ||
</button> | ||
); | ||
} |
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