update filters for table

This commit is contained in:
Alexander Navarro 2023-09-23 18:51:45 -03:00
parent c2a8f749b4
commit 71cf3d656e
2 changed files with 91 additions and 5 deletions

View file

@ -1,12 +1,19 @@
import React from 'react';
import React, { useMemo, useState } from 'react';
import usePagination, { Offset } from 'src/hooks/usePagination';
export type DataItem = Record<string, any>;
export enum HeaderType {
Index,
String,
Number,
}
export interface Header {
key: string;
header: string;
formatter?: (data: DataItem) => string;
type: HeaderType;
}
interface Props {
@ -14,8 +21,50 @@ interface Props {
headers: Header[];
}
interface Filter {
type: HeaderType,
value: string,
}
export default function Table({ data, headers }: Props): JSX.Element {
const { items, changeOffset } = usePagination<DataItem>({ items: data });
const [filters, setFilters] = useState<Record<string, Filter>>({});
const resolveFilterByType = (filter: Filter, data: any): boolean => {
switch (filter.type) {
case HeaderType.String:
return data.search(filter.value) !== -1;
case HeaderType.Number:
return data === filter.value;
}
return true;
}
const filteredItems = useMemo(() => {
return data.filter((item) => {
return Object.entries(filters).every(
([key, filter]) => resolveFilterByType(filter, item[key]),
);
});
}, [data, filters]);
const { items, changeOffset } = usePagination<DataItem>({
items: filteredItems,
});
const handleUpdateFilters = (name: string, type: HeaderType, value: any): void => {
setFilters((prev) => {
if (value === null) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete prev[name];
return { ...prev };
}
return { ...prev, [name]: { value, type } };
});
};
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)
@ -31,13 +80,45 @@ export default function Table({ data, headers }: Props): JSX.Element {
return data[header.key];
}
function formatFilter(header: Header): JSX.Element {
switch (header.type) {
case HeaderType.String:
return (
<input
onChange={(e) => {
handleUpdateFilters(header.key, header.type, e.target.value ?? null);
}}
/>
);
case HeaderType.Number:
return (
<input
type="number"
onChange={(e) => {
handleUpdateFilters(header.key, header.type, e.target.value !== '' ? parseInt(e.target.value) : null);
}}
/>
);
default:
break;
}
return <></>;
}
return (
<>
<table>
<table style={{ width: '100%' }}>
<thead>
<tr>
{headers.map((item, idx) => (
<th key={idx}>{item.header}</th>
<th key={idx}>
<div className="vstack">
{item.header}
{formatFilter(item)}
</div>
</th>
))}
</tr>
</thead>

View file

@ -1,7 +1,7 @@
---
import { getCollection } from 'astro:content';
import Layout from '@layouts/Layout.astro';
import Table, { type Header } from '@components/Table.jsx';
import Table, { HeaderType, type Header } from '@components/Table.jsx';
const rawGames = await getCollection('games');
@ -15,23 +15,28 @@ const headers: Header[] = [
{
key: 'id',
header: 'index',
type: HeaderType.Index,
},
{
key: 'title',
header: 'Title',
formatter: (data) => `<a href="games/${data.slug}">${data.title}</a>`,
type: HeaderType.String,
},
{
key: 'status',
header: 'Status',
type: HeaderType.String,
},
{
key: 'times_played',
header: 'Times Played',
type: HeaderType.Number,
},
{
key: 'registered_hours',
header: 'Registered Hours',
type: HeaderType.Number,
},
];
---