Files
encoach_frontend/src/hooks/usePagination.tsx

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 };
}