update filters for table
This commit is contained in:
parent
c2a8f749b4
commit
71cf3d656e
2 changed files with 91 additions and 5 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue