chore: update Pagination component

This commit is contained in:
ValdiANS
2025-12-04 22:44:43 +07:00
parent 79a89ea193
commit fd024fdb8f
+164 -74
View File
@@ -1,7 +1,9 @@
'use client'; 'use client';
import { ReactNode } from 'react'; import { ChangeEventHandler, ReactNode } from 'react';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import Button from '@/components/Button';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -17,16 +19,18 @@ const PaginationButton = ({
disabled?: boolean; disabled?: boolean;
onClick?: () => void; onClick?: () => void;
}) => ( }) => (
<button <Button
className={cn( variant='ghost'
'join-item btn btn-ghost p-2.5 rounded-lg text-sm font-medium text-gray-500 aspect-square', color='none'
'disabled:text-gray-700 disabled:pointer-events-auto! disabled:cursor-not-allowed! disabled:bg-gray-50 disabled:active:translate-y-0'
)}
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}
className={cn(
'join-item w-10 h-10 grid place-items-center p-2.5 rounded-lg text-sm font-semibold text-base-content/50 aspect-square',
'disabled:text-primary disabled:pointer-events-auto! disabled:cursor-not-allowed! disabled:bg-primary/10 disabled:active:translate-y-0'
)}
> >
{content} {content}
</button> </Button>
); );
const EtcPaginationButton = ({ const EtcPaginationButton = ({
@@ -90,16 +94,20 @@ const Pagination = ({
currentPage = 1, currentPage = 1,
totalItems = 0, totalItems = 0,
itemsPerPage = 10, itemsPerPage = 10,
rowOptions = [10, 20, 50, 100],
onPageChange, onPageChange,
onPrevPage = () => {}, onPrevPage = () => {},
onNextPage = () => {}, onNextPage = () => {},
onRowChange,
}: { }: {
currentPage: number; currentPage: number;
totalItems: number; totalItems: number;
itemsPerPage: number; itemsPerPage: number;
rowOptions?: number[];
onPageChange: (pageNumber: number) => void; onPageChange: (pageNumber: number) => void;
onPrevPage: () => void; onPrevPage: () => void;
onNextPage: () => void; onNextPage: () => void;
onRowChange?: (row: number) => void;
}) => { }) => {
const totalPages = const totalPages =
Math.ceil(totalItems / itemsPerPage) === 0 Math.ceil(totalItems / itemsPerPage) === 0
@@ -107,30 +115,139 @@ const Pagination = ({
: Math.ceil(totalItems / itemsPerPage); : Math.ceil(totalItems / itemsPerPage);
const pageChangeHandler = (pageNumber: number) => onPageChange(pageNumber); const pageChangeHandler = (pageNumber: number) => onPageChange(pageNumber);
const firstPageClickHandler = () => onPageChange(1);
const lastPageClickHandler = () => onPageChange(totalPages);
return ( const rowChangeHandler: ChangeEventHandler<HTMLSelectElement> = (e) => {
<div> onRowChange?.(Number(e.target.value));
<div className='join w-full justify-between items-center gap-3'> };
<button
const DisplayedRowCountSelect = () => (
<div className='flex flex-row items-center gap-4'>
<span className='text-sm font-medium text-base-content/50'>Showing</span>
<select
defaultValue={itemsPerPage}
onChange={rowChangeHandler}
className='select select-xs w-fit text-base-content/50'
>
{rowOptions.map((rowOption, rowOptionIdx) => (
<option
key={rowOptionIdx}
value={rowOption}
className='text-base-content active:text-neutral-content'
>
{rowOption} Per page
</option>
))}
</select>
</div>
);
const GoToFirstPageButton = () => (
<Button
disabled={currentPage === 1} disabled={currentPage === 1}
onClick={onPrevPage} onClick={firstPageClickHandler}
variant='ghost'
color='none'
className={cn( className={cn(
'join-item btn btn-outline group px-3 py-2 text-sm font-semibold rounded-lg border border-gray-300 shadow-xs hidden sm:flex justify-center items-center gap-1.5', 'join-item w-10 h-10 grid place-items-center p-2.5 rounded-lg text-sm font-semibold text-base-content/50 aspect-square',
'disabled:bg-[initial]! disabled:text-gray-400 disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0' 'disabled:bg-[initial]! disabled:text-base-content disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)} )}
> >
<Icon <Icon
icon='uil:arrow-left' icon='heroicons:chevron-double-left'
width={20} width={20}
height={20} height={20}
className='text-gray-400 group-disabled:text-gray-300' className='text-gray-400 group-disabled:text-gray-300'
/>{' '} />
Previous </Button>
</button> );
{totalPages <= 7 && ( const PrevPageButton = () => (
<div className='join-item join gap-0.5'> <Button
{range(1, totalPages).map((pageNumber) => ( disabled={currentPage === 1}
onClick={onPrevPage}
variant='ghost'
color='none'
className={cn(
'join-item w-10 h-10 grid place-items-center p-2.5 rounded-lg text-sm font-semibold text-base-content/50 aspect-square',
'disabled:bg-[initial]! disabled:text-base-content disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)}
>
<Icon
icon='heroicons:chevron-left'
width={20}
height={20}
className='text-gray-400 group-disabled:text-gray-300'
/>
</Button>
);
const GoToLastPageButton = () => (
<Button
variant='ghost'
color='none'
disabled={currentPage === totalPages}
onClick={lastPageClickHandler}
className={cn(
'join-item w-10 h-10 grid place-items-center p-2.5 rounded-lg text-sm font-semibold text-base-content/50 aspect-square',
'disabled:bg-[initial]! disabled:text-base-content disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)}
>
<Icon
icon='heroicons:chevron-double-right'
width={20}
height={20}
className='text-gray-400 group-disabled:text-gray-300'
/>
</Button>
);
const NextPageButton = () => (
<Button
variant='ghost'
color='none'
disabled={currentPage === totalPages}
onClick={onNextPage}
className={cn(
'join-item w-10 h-10 grid place-items-center p-2.5 rounded-lg text-sm font-semibold text-base-content/50 aspect-square',
'disabled:bg-[initial]! disabled:text-base-content disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)}
>
<Icon
icon='heroicons:chevron-right'
width={20}
height={20}
className='text-gray-400 group-disabled:text-gray-300'
/>
</Button>
);
const PageInfo = () => (
<span className='text-nowrap text-sm font-medium text-base-content/50'>
Page {currentPage} of {totalPages}
</span>
);
return (
<div className='@container'>
<div className='flex flex-row justify-center items-center'>
<div className='hidden @lg:block'>
<DisplayedRowCountSelect />
</div>
<div className='join w-full justify-end @lg:justify-center items-center gap-0.5'>
<div className='hidden @lg:block'>
<GoToFirstPageButton />
</div>
<div className='hidden @lg:block'>
<PrevPageButton />
</div>
{totalPages <= 7 &&
range(1, totalPages).map((pageNumber) => (
<PaginationButton <PaginationButton
key={pageNumber} key={pageNumber}
content={pageNumber} content={pageNumber}
@@ -138,11 +255,9 @@ const Pagination = ({
onClick={() => pageChangeHandler(pageNumber)} onClick={() => pageChangeHandler(pageNumber)}
/> />
))} ))}
</div>
)}
{totalPages > 7 && ( {totalPages > 7 && (
<div className='join-item join gap-0.5'> <>
<PaginationButton <PaginationButton
content={1} content={1}
disabled={currentPage === 1} disabled={currentPage === 1}
@@ -272,61 +387,36 @@ const Pagination = ({
onClick={() => pageChangeHandler(totalPages)} onClick={() => pageChangeHandler(totalPages)}
/> />
)} )}
</div> </>
)} )}
<button <div className='hidden @lg:block'>
disabled={currentPage === totalPages} <NextPageButton />
onClick={onNextPage}
className={cn(
'join-item btn btn-outline group px-3 py-2 text-sm font-semibold rounded-lg border border-gray-300 shadow-xs hidden sm:flex justify-center items-center gap-1.5',
'disabled:bg-[initial]! disabled:text-gray-400 disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)}
>
Next{' '}
<Icon
icon='uil:arrow-right'
width={20}
height={20}
className='text-gray-400 group-disabled:text-gray-300'
/>
</button>
</div> </div>
<div className='flex gap-2 mt-2 sm:hidden'> <div className='hidden @lg:block'>
<button <GoToLastPageButton />
disabled={currentPage === 1} </div>
onClick={onPrevPage} </div>
className={cn(
'join-item btn btn-outline group px-3 py-2 text-sm font-semibold rounded-lg border border-gray-300 shadow-xs flex justify-center items-center gap-1.5',
'disabled:bg-[initial]! disabled:text-gray-400 disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0'
)}
>
<Icon
icon='uil:arrow-left'
width={20}
height={20}
className='text-gray-400 group-disabled:text-gray-300'
/>{' '}
Previous
</button>
<button <div className='hidden @lg:block'>
disabled={currentPage === totalPages} <PageInfo />
onClick={onNextPage} </div>
className={cn( </div>
'join-item btn btn-outline group px-3 py-2 text-sm font-semibold rounded-lg border border-gray-300 shadow-xs flex justify-center items-center gap-1.5',
'disabled:bg-[initial]! disabled:text-gray-400 disabled:pointer-events-auto! disabled:cursor-not-allowed disabled:active:translate-y-0' <div className='flex @lg:hidden flex-col justify-center items-end gap-2'>
)} <div className='flex flex-row items-center gap-0.5'>
> <GoToFirstPageButton />
Next{' '} <PrevPageButton />
<Icon <NextPageButton />
icon='uil:arrow-right' <GoToLastPageButton />
width={20} </div>
height={20}
className='text-gray-400 group-disabled:text-gray-300' <div className='flex flex-row items-center gap-4'>
/> <DisplayedRowCountSelect />
</button>
<PageInfo />
</div>
</div> </div>
</div> </div>
); );