Core/src/Components/Pagination/Paginate.tsx
Madeorsk e2fc741fee
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
Add dispatch function to useAsync to allow synchronous editing of async data after first retrieval.
2024-09-24 22:51:41 +02:00

137 lines
2.7 KiB
TypeScript

import React, {useCallback, useState} from "react";
import {Pagination} from "./Pagination";
import {SpinningLoader} from "../Loaders/SpinningLoader";
import {Await, useAsync} from "../../Async";
/**
* Paginated content component with custom page handling.
*/
export function Paginate({ page, onChange, count, children }: React.PropsWithChildren<{
/**
* The current page.
*/
page: number;
/**
* Called when a new page is selected.
* @param newPage The newly selected page.
*/
onChange: (newPage: number) => void;
/**
* Pages count.
*/
count: number;
}>)
{
return (
<>
{children}
<Pagination page={page} onChange={onChange} count={count} />
</>
);
}
/**
* Paginated content component.
*/
export function AutoPaginate({ count, children }: {
/**
* Pages count.
*/
count: number;
/**
* Show the given page.
* @param page The page to show.
*/
children: (page: number) => React.ReactElement;
})
{
// The current page.
const [page, setPage] = useState<number>(1);
return (
<Paginate page={page} onChange={setPage} count={count}>
{children(page)}
</Paginate>
);
}
/**
* Asynchronous paginated content component.
*/
export function AsyncPaginate<T>({ count, getData, children }: {
/**
* Get pages count.
*/
count: () => Promise<number>;
/**
* Get data for the given page.
* @param page The page for which to get data.
*/
getData: (page: number) => Promise<T>;
/**
* Show the current page with its retrieved data.
* @param data Data of the page to show.
* @param page The page to show.
*/
children: (data: T, page: number) => React.ReactElement;
})
{
// Getting pages count.
const [asyncCount] = useAsync(count, []);
return (
<Await async={asyncCount} fallback={<SpinningLoader />}>
{
(count) => (
<AutoPaginate count={count}>
{(page) => <AsyncPage page={page} getData={getData} render={children} />}
</AutoPaginate>
)
}
</Await>
);
}
/**
* An async page to render.
*/
export function AsyncPage<T>({page, getData, render}: {
/**
* The page number to show.
*/
page: number;
/**
* Get data for the given page.
* @param page The page for which to get data.
*/
getData: (page: number) => Promise<T>;
/**
* Render the page with its retrieved data.
* @param data Data of the page to show.
* @param page The page to show.
*/
render: (data: T, page: number) => React.ReactElement;
})
{
// Store function to get page data.
const getPageData = useCallback(() => {
return getData(page);
}, [page]);
// Getting page data.
const [asyncPageData] = useAsync(getPageData, [getPageData]);
return (
<Await async={asyncPageData} fallback={<SpinningLoader />}>
{(pageData) => render(pageData, page)}
</Await>
);
}