138 lines
2.7 KiB
TypeScript
138 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>
|
||
|
);
|
||
|
}
|