139 lines
4.4 KiB
TypeScript
139 lines
4.4 KiB
TypeScript
import Button from "@/components/Low/Button";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import {
|
|
BsChevronDoubleLeft,
|
|
BsChevronDoubleRight,
|
|
BsChevronLeft,
|
|
BsChevronRight,
|
|
} from "react-icons/bs";
|
|
import Select from "../components/Low/Select";
|
|
|
|
export default function usePagination<T>(list: T[], size = 25) {
|
|
const [page, setPage] = useState(0);
|
|
const [itemsPerPage, setItemsPerPage] = useState(size);
|
|
|
|
const items = useMemo(
|
|
() => list.slice(page * itemsPerPage, (page + 1) * itemsPerPage),
|
|
[list, page, itemsPerPage]
|
|
);
|
|
useEffect(() => {
|
|
if (page * itemsPerPage >= list.length) setPage(0);
|
|
}, [items, itemsPerPage, list.length, page]);
|
|
|
|
const itemsPerPageOptions = [25, 50, 100, 200];
|
|
|
|
const render = () => (
|
|
<div className="w-full flex gap-2 justify-between items-center">
|
|
<div className="flex items-center gap-4 w-fit">
|
|
<Button
|
|
className="w-[200px] h-fit"
|
|
disabled={page === 0}
|
|
onClick={() => setPage((prev) => prev - 1)}
|
|
>
|
|
Previous Page
|
|
</Button>
|
|
</div>
|
|
<div className="flex items-center gap-4 w-fit">
|
|
<div className="flex flex-row items-center gap-1 w-56">
|
|
<Select
|
|
value={{
|
|
value: itemsPerPage.toString(),
|
|
label: itemsPerPage.toString(),
|
|
}}
|
|
onChange={(value) =>
|
|
setItemsPerPage(parseInt(value!.value ?? "25"))
|
|
}
|
|
options={itemsPerPageOptions.map((size) => ({
|
|
label: size.toString(),
|
|
value: size.toString(),
|
|
}))}
|
|
isClearable={false}
|
|
styles={{
|
|
control: (styles) => ({ ...styles, width: "100px" }),
|
|
container: (styles) => ({ ...styles, width: "100px" }),
|
|
}}
|
|
/>
|
|
<span className="opacity-80 w-32 text-center">
|
|
{page * itemsPerPage + 1} -{" "}
|
|
{itemsPerPage * (page + 1) > list.length
|
|
? list.length
|
|
: itemsPerPage * (page + 1)}
|
|
{list.length}
|
|
</span>
|
|
</div>
|
|
<Button
|
|
className="w-[200px]"
|
|
disabled={(page + 1) * itemsPerPage >= list.length}
|
|
onClick={() => setPage((prev) => prev + 1)}
|
|
>
|
|
Next Page
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderMinimal = () => (
|
|
<div className="flex gap-4 items-center">
|
|
<div className="flex gap-2 items-center">
|
|
<button
|
|
disabled={page === 0}
|
|
onClick={() => setPage(0)}
|
|
className="disabled:opacity-60 disabled:cursor-not-allowed"
|
|
>
|
|
<BsChevronDoubleLeft />
|
|
</button>
|
|
<button
|
|
disabled={page === 0}
|
|
onClick={() => setPage((prev) => prev - 1)}
|
|
className="disabled:opacity-60 disabled:cursor-not-allowed"
|
|
>
|
|
<BsChevronLeft />
|
|
</button>
|
|
</div>
|
|
<div className="flex flex-row items-center gap-1 w-56">
|
|
<Select
|
|
value={{
|
|
value: itemsPerPage.toString(),
|
|
label: itemsPerPage.toString(),
|
|
}}
|
|
onChange={(value) => setItemsPerPage(parseInt(value!.value ?? "25"))}
|
|
options={itemsPerPageOptions.map((size) => ({
|
|
label: size.toString(),
|
|
value: size.toString(),
|
|
}))}
|
|
isClearable={false}
|
|
styles={{
|
|
control: (styles) => ({ ...styles, width: "100px" }),
|
|
container: (styles) => ({ ...styles, width: "100px" }),
|
|
}}
|
|
/>
|
|
<span className="opacity-80 w-32 text-center">
|
|
{page * itemsPerPage + 1} -{" "}
|
|
{itemsPerPage * (page + 1) > list.length
|
|
? list.length
|
|
: itemsPerPage * (page + 1)}
|
|
/ {list.length}
|
|
</span>
|
|
</div>
|
|
<div className="flex gap-2 items-center">
|
|
<button
|
|
disabled={(page + 1) * itemsPerPage >= list.length}
|
|
onClick={() => setPage((prev) => prev + 1)}
|
|
className="disabled:opacity-60 disabled:cursor-not-allowed"
|
|
>
|
|
<BsChevronRight />
|
|
</button>
|
|
<button
|
|
disabled={(page + 1) * itemsPerPage >= list.length}
|
|
onClick={() => setPage(Math.floor(list.length / itemsPerPage))}
|
|
className="disabled:opacity-60 disabled:cursor-not-allowed"
|
|
>
|
|
<BsChevronDoubleRight />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return { page, items, setPage, render, renderMinimal };
|
|
}
|