first
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
// PaginationBar.tsx
|
||||
import type { UseDataControlReturn } from 'hooks/useDataControl';
|
||||
import { useMemo } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
|
||||
type PaginationBarProps<T> = {
|
||||
dataManager: UseDataControlReturn<T>;
|
||||
};
|
||||
|
||||
export const PaginationBar = <T,>(props: PaginationBarProps<T>) => {
|
||||
const { currentPage, pageCount, changePage, pageSize, setPageSize } = props.dataManager;
|
||||
|
||||
const pageSizeOptions = useMemo(() => [5, 10, 15, 20], []);
|
||||
|
||||
const handlePageSizeChange = (value: string) => {
|
||||
const next = Number(value);
|
||||
if (!Number.isFinite(next) || next <= 0) return;
|
||||
setPageSize(next);
|
||||
changePage(1);
|
||||
};
|
||||
|
||||
const buttonClass =
|
||||
'h-8 w-8 p-0 hover:bg-white text-muted-foreground disabled:opacity-100 disabled:pointer-events-none';
|
||||
|
||||
return (
|
||||
<div className="bg-muted/50 flex w-full items-center justify-between border-t p-4">
|
||||
{/* LEFT */}
|
||||
<div className="flex items-center gap-2 min-[500px]:gap-3 text-sm text-muted-foreground whitespace-nowrap">
|
||||
<span className="hidden min-[500px]:inline">
|
||||
Showing page {currentPage} of {pageCount}
|
||||
</span>
|
||||
<span className="min-[500px]:hidden">
|
||||
p {currentPage}/{pageCount}
|
||||
</span>
|
||||
|
||||
<span className="bg-border h-5 w-px" aria-hidden="true" />
|
||||
|
||||
<Select value={String(pageSize)} onValueChange={handlePageSizeChange}>
|
||||
<SelectTrigger
|
||||
className="
|
||||
h-8
|
||||
w-[80px] min-[500px]:w-[110px]
|
||||
justify-between
|
||||
bg-transparent
|
||||
text-muted-foreground
|
||||
hover:bg-white
|
||||
data-[state=open]:bg-muted
|
||||
"
|
||||
aria-label="Rows per page"
|
||||
>
|
||||
<SelectValue className="text-muted-foreground">
|
||||
<span className="hidden min-[500px]:inline">{pageSize} / page</span>
|
||||
<span className="min-[500px]:hidden">{pageSize}/p</span>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent align="start">
|
||||
{pageSizeOptions.map((n) => (
|
||||
<SelectItem
|
||||
key={n}
|
||||
value={String(n)}
|
||||
className="
|
||||
text-muted-foreground
|
||||
data-[state=checked]:bg-muted
|
||||
data-[state=checked]:text-muted-foreground
|
||||
"
|
||||
>
|
||||
<span className="hidden min-[500px]:inline">{n} / page</span>
|
||||
<span className="min-[500px]:hidden">{n}/p</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* RIGHT */}
|
||||
<div className="flex items-center gap-0.5 min-[500px]:gap-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className={buttonClass}
|
||||
onClick={() => changePage(1)}
|
||||
disabled={currentPage === 1}
|
||||
aria-label="First"
|
||||
>
|
||||
«
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className={buttonClass}
|
||||
onClick={() => changePage(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
aria-label="Prev"
|
||||
>
|
||||
‹
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className={buttonClass}
|
||||
onClick={() => changePage(currentPage + 1)}
|
||||
disabled={currentPage === pageCount}
|
||||
aria-label="Next"
|
||||
>
|
||||
›
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className={buttonClass}
|
||||
onClick={() => changePage(pageCount)}
|
||||
disabled={currentPage === pageCount}
|
||||
aria-label="Last"
|
||||
>
|
||||
»
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user