Update pagination in table.

Add getPaginationRage to usePagination hook.
Use the getPaginationRage to create dynamic buttons in the table.
This commit is contained in:
Alexander Navarro 2023-11-05 10:32:49 -03:00
parent b1979d7351
commit e593354cb4
4 changed files with 80 additions and 26 deletions

View file

@ -68,3 +68,12 @@ td {
border: 1px solid white;
text-align: center;
}
.btn {
padding: var(--prj-spacing-1);
}
.btn-primary {
background-color: var(--prj--primary-bg);
color: var(--prj--primary-text);
}

View file

@ -43,8 +43,9 @@ export default function Table({ data, headers }: Props): JSX.Element {
});
}, [data, filters]);
const { items, changeOffset } = usePagination<DataItem>({
const { items, changeOffset, getPaginationRange } = usePagination<DataItem>({
items: filteredItems,
limit: 10,
});
const handleUpdateFilters = (
@ -179,6 +180,19 @@ export default function Table({ data, headers }: Props): JSX.Element {
>
Prev
</button>
{getPaginationRange().map((item) => (
<button
className={item.current ? 'btn-primary' : ''}
key={item.page}
onClick={() => {
changeOffset(Offset.To, item.page);
}}
>
{item.page}
</button>
))}
<button
onClick={() => {
changeOffset(Offset.Next);

View file

@ -1,12 +1,19 @@
import { useReducer } from 'react';
interface Page {
page: number;
current: boolean;
}
export interface IUsePagination<T> {
items: T[];
changeOffset: (offset: Offset) => void;
changeOffset: (offset: Offset, newValue?: number) => void;
getPaginationRange: () => Page[];
}
interface Props<T> {
items: T[];
limit: number;
}
interface State {
@ -24,10 +31,12 @@ export enum Offset {
Prev,
First,
Last,
To,
}
export default function usePagination<T>({
items,
limit = 30,
}: Props<T>): IUsePagination<T> {
const reducer = (state: State, action: ActionType): State => {
switch (action.type) {
@ -44,47 +53,69 @@ export default function usePagination<T>({
const [state, dispatch] = useReducer(reducer, {
offset: 0,
total: 0,
limit: 30,
limit,
});
const changeOffset = (offset: Offset): void => {
const changeOffset = (offset: Offset, newValue: number = 1): void => {
let value = 0;
switch (offset) {
case Offset.Next:
dispatch({
type: 'update',
name: 'offset',
value: state.offset + state.limit,
});
value = state.offset + state.limit;
break;
case Offset.Prev:
dispatch({
type: 'update',
name: 'offset',
value: state.offset - state.limit,
});
value = state.offset - state.limit;
break;
case Offset.First:
dispatch({
type: 'update',
name: 'offset',
value: 0,
});
value = 0;
break;
case Offset.Last:
dispatch({
type: 'update',
name: 'offset',
value: items.length - state.limit,
});
value = items.length - state.limit;
break;
case Offset.To:
value = (newValue - 1) * state.limit;
break;
default:
break;
}
if (value < 0 || value > items.length - state.limit) {
return;
}
dispatch({
type: 'update',
name: 'offset',
value,
});
};
/** Returns an array with the aviables pages to directly navigate.
* This pages are centered in the current page and offest to 5 items to each side.
*/
const getPaginationRange = (): Page[] => {
// NOTE: this is made to work with uneven numbers,
// So the current page is always aligned in the center.
const paginationToSides = 5;
const currentPage = Math.ceil(state.offset / state.limit) + 1;
const lastPage = Math.ceil(items.length / state.limit);
const start = Math.max(currentPage - paginationToSides - 1, 0);
const end = Math.min(currentPage + paginationToSides, lastPage);
return Array.from(
{
length: end - start,
},
(_, idx) => {
const page = idx + 1 + start;
return { page, current: page === currentPage };
},
);
};
return {
changeOffset,
getPaginationRange,
items: items.slice(state.offset, state.offset + state.limit),
};
}

View file

@ -7,7 +7,7 @@ const rawGames = await getCollection('games');
const games = rawGames.map((item, idx) => ({
...item.data,
id: idx,
id: idx + 1,
slug: item.slug,
}));