update table component
This commit is contained in:
parent
176279ecb5
commit
41d682e9df
4 changed files with 167 additions and 28 deletions
|
|
@ -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
|
||||||
90
src/hooks/usePagination.tsx
Normal file
90
src/hooks/usePagination.tsx
Normal 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue