update table component

This commit is contained in:
Alexander Navarro 2023-09-16 12:46:23 -03:00
parent 176279ecb5
commit 41d682e9df
4 changed files with 167 additions and 28 deletions

View file

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import usePagination, { Offset } from 'src/hooks/usePagination';
type DataItem = Record<string, any>; export type DataItem = Record<string, any>;
export interface Header { export interface Header {
key: string; key: string;
header: string; header: string;
formatter?: (data: DataItem) => JSX.ElementType; formatter?: (data: DataItem) => string;
} }
interface Props { interface Props {
@ -14,15 +15,24 @@ interface Props {
} }
export default function Table({ data, headers }: Props): JSX.Element { export default function Table({ data, headers }: Props): JSX.Element {
function getProperty(data: DataItem, header: Header): any { const { items, changeOffset } = usePagination<DataItem>({ items: data });
if (header.key === 'index') {
return 'index'; function formatCell(data: DataItem, header: Header): JSX.Element {
// This formatting is only used because the source is trusted (private markdown files manage only by me)
// and because Astro don't allow me to pass JSX from an Astro file to a TSX file,
// so I have to pass the formatted row as a string.
// DON'T use this method on a public API
if (header.formatter != null) {
return (
<div dangerouslySetInnerHTML={{ __html: header.formatter(data) }} />
);
} }
return data[header.key]; return data[header.key];
} }
return ( return (
<>
<table> <table>
<thead> <thead>
<tr> <tr>
@ -33,14 +43,46 @@ export default function Table({ data, headers }: Props): JSX.Element {
</thead> </thead>
<tbody> <tbody>
{data.map((item, idx) => ( {items.map((item, idx) => (
<tr key={idx}> <tr key={idx}>
{headers.map((header, hidx) => ( {headers.map((header, hidx) => (
<td key={hidx}>{getProperty(item.data, header)}</td> <td key={hidx}>{formatCell(item, header)}</td>
))} ))}
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
<section>
<button
onClick={() => {
changeOffset(Offset.First);
}}
>
First
</button>
<button
onClick={() => {
changeOffset(Offset.Prev);
}}
>
Prev
</button>
<button
onClick={() => {
changeOffset(Offset.Next);
}}
>
Next
</button>
<button
onClick={() => {
changeOffset(Offset.Last);
}}
>
Last
</button>
</section>
</>
); );
} }

@ -1 +1 @@
Subproject commit efff561a802a40e93493d01a50ef228bfe0c485e Subproject commit 65edd2d35506ff4169b2ab6db1e48c167f64d73e

View file

@ -0,0 +1,90 @@
import { useReducer } from 'react';
export interface IUsePagination<T> {
items: T[];
changeOffset: (offset: Offset) => void;
}
interface Props<T> {
items: T[];
}
interface State {
offset: number;
total: number;
limit: number;
}
type ActionType =
| { type: 'update'; value: any; name: string }
| { type: 'update'; value: any; name: string };
export enum Offset {
Next,
Prev,
First,
Last,
}
export default function usePagination<T>({
items,
}: Props<T>): IUsePagination<T> {
const reducer = (state: State, action: ActionType): State => {
switch (action.type) {
case 'update':
return {
...state,
[action.name]: action.value,
};
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, {
offset: 0,
total: 0,
limit: 30,
});
const changeOffset = (offset: Offset): void => {
switch (offset) {
case Offset.Next:
dispatch({
type: 'update',
name: 'offset',
value: state.offset + state.limit,
});
break;
case Offset.Prev:
dispatch({
type: 'update',
name: 'offset',
value: state.offset - state.limit,
});
break;
case Offset.First:
dispatch({
type: 'update',
name: 'offset',
value: 0,
});
break;
case Offset.Last:
dispatch({
type: 'update',
name: 'offset',
value: items.length - state.limit,
});
break;
default:
break;
}
};
return {
changeOffset,
items: items.slice(state.offset, state.offset + state.limit),
};
}

View file

@ -3,16 +3,23 @@ import { getCollection } from 'astro:content';
import Layout from '@layouts/Layout.astro'; import Layout from '@layouts/Layout.astro';
import Table, { type Header } from '@components/Table.jsx'; import Table, { type Header } from '@components/Table.jsx';
const games = await getCollection('games'); const rawGames = await getCollection('games');
const games = rawGames.map((item, idx) => ({
...item.data,
id: idx,
slug: item.slug,
}));
const headers: Header[] = [ const headers: Header[] = [
{ {
key: 'index', key: 'id',
header: 'index', header: 'index',
}, },
{ {
key: 'title', key: 'title',
header: 'Title', header: 'Title',
formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
}, },
{ {
key: 'status', key: 'status',
@ -29,11 +36,11 @@ const headers: Header[] = [
]; ];
--- ---
<Layout title="aleidk"> <Layout title="List of games">
<h1>Games</h1> <h1>Games</h1>
<section> <section>
<Table data={games} headers={headers} /> <Table client:load data={games} headers={headers} />
</section> </section>
<section> <section>
@ -41,7 +48,7 @@ const headers: Header[] = [
{ {
games.map((item) => ( games.map((item) => (
<li> <li>
<a href={`games/${item.slug}`}>{item.data.title}</a> <a href={`games/${item.slug}`}>{item.title}</a>
</li> </li>
)) ))
} }